diff --git a/bundles/automation/org.eclipse.smarthome.automation.api/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.api/META-INF/MANIFEST.MF index 7b4978ccb6f..9a8adef988a 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.api/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.api/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome Import-Package: org.eclipse.smarthome.automation, + org.eclipse.smarthome.automation.dto, org.eclipse.smarthome.automation.events, org.eclipse.smarthome.automation.handler, org.eclipse.smarthome.automation.parser, diff --git a/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/ManagedRuleProvider.java b/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/ManagedRuleProvider.java index da467c62f79..d4bab33ea66 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/ManagedRuleProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/ManagedRuleProvider.java @@ -7,8 +7,6 @@ */ package org.eclipse.smarthome.automation; -import org.eclipse.smarthome.automation.Rule; -import org.eclipse.smarthome.automation.RuleProvider; import org.eclipse.smarthome.core.common.registry.DefaultAbstractManagedProvider; /** @@ -20,11 +18,6 @@ */ public class ManagedRuleProvider extends DefaultAbstractManagedProvider implements RuleProvider { - @Override - protected String getKey(Rule element) { - return element.getUID(); - } - @Override protected String getStorageName() { return "automation_rules"; diff --git a/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/template/RuleTemplate.java b/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/template/RuleTemplate.java index c80034fe847..d3d253d1c10 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/template/RuleTemplate.java +++ b/bundles/automation/org.eclipse.smarthome.automation.api/src/main/java/org/eclipse/smarthome/automation/template/RuleTemplate.java @@ -1,10 +1,10 @@ /** - * Copyright (c) 1997, 2015 by ProSyst Software GmbH and others. - * 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 - */ +* Copyright (c) 2015, 2017 by Bosch Software Innovations and others. +* 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.eclipse.smarthome.automation.template; import java.util.ArrayList; @@ -241,4 +241,33 @@ public List getActions() { return actions != null ? actions : Collections. emptyList(); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uid == null) ? 0 : uid.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RuleTemplate)) { + return false; + } + RuleTemplate other = (RuleTemplate) obj; + if (uid == null) { + if (other.uid != null) { + return false; + } + } else if (!uid.equals(other.uid)) { + return false; + } + return true; + } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.core/META-INF/MANIFEST.MF index f66f2401daf..985dac05d3d 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.core/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome Import-Package: com.google.gson, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.automation, org.eclipse.smarthome.automation.core.util, org.eclipse.smarthome.automation.events, diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/Connection.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/Connection.java index 6ed0c33a61c..74a4884af8c 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/Connection.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/Connection.java @@ -144,7 +144,7 @@ public static Set getConnections(Map inputs, Logger connections.add(connection); } else { - log.error("Wrong format of Output : " + inputName + ": " + output); + log.error("Wrong format of Output : {}: {}", inputName, output); continue; } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/ReferenceResolverUtil.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/ReferenceResolverUtil.java index 968e2f9e908..29e721ce7a2 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/ReferenceResolverUtil.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/ReferenceResolverUtil.java @@ -213,8 +213,8 @@ private static String resolvePattern(String reference, Map context) { end = reference.indexOf('}', start + 2); if (end == -1) { previous = start; - logger.warn("Couldn't parse referenced key: " + reference.substring(start) - + ": expected reference syntax-> ${referencedKey}"); + logger.warn("Couldn't parse referenced key: {}: expected reference syntax-> ${referencedKey}", + reference.substring(start)); break; } final String referencedKey = reference.substring(start + 2, end); diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleEngine.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleEngine.java index b54f4efc55c..7a004f573dd 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleEngine.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleEngine.java @@ -23,7 +23,7 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; - +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.automation.Action; import org.eclipse.smarthome.automation.Condition; import org.eclipse.smarthome.automation.Module; @@ -694,7 +694,7 @@ protected void setRuleEnabled(Rule rule, boolean isEnabled) { if (status == RuleStatus.DISABLED) { setRule(runtimeRule); } else { - logger.debug("The rule rId = " + rUID + " is already enabled."); + logger.debug("The rule rId = {} is already enabled.", rUID); } } else { unregister(runtimeRule); @@ -1087,7 +1087,7 @@ protected synchronized RuleStatusInfo getRuleStatusInfo(String rUID) { return statusMap.get(rUID); } - protected synchronized String getUniqueId() { + protected synchronized @NonNull String getUniqueId() { int result = 0; if (rules != null) { Set col = rules.keySet(); @@ -1133,7 +1133,7 @@ protected void scheduleRulesConfigurationUpdated(Map config) { if (value instanceof Number) { scheduleReinitializationDelay = ((Number) value).longValue(); } else { - logger.error("Invalid configuration value: " + value + "It MUST be Number."); + logger.error("Invalid configuration value: {}. It MUST be Number.", value); } } else { scheduleReinitializationDelay = DEFAULT_REINITIALIZATION_DELAY; diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleRegistryImpl.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleRegistryImpl.java index 00ebd89ccb1..9a754778ec2 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleRegistryImpl.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/RuleRegistryImpl.java @@ -15,9 +15,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.automation.Rule; import org.eclipse.smarthome.automation.RuleProvider; import org.eclipse.smarthome.automation.RuleRegistry; @@ -231,9 +231,6 @@ protected void removeModuleHandlerFactory(ModuleHandlerFactory moduleHandlerFact */ @Override public Rule add(Rule rule) { - if (rule == null) { - throw new IllegalArgumentException("The added rule must not be null!"); - } String rUID = rule.getUID(); if (rUID == null) { rUID = ruleEngine.getUniqueId(); @@ -241,7 +238,12 @@ public Rule add(Rule rule) { } else { super.add(rule); } - return get(rUID); + Rule ruleCopy = get(rUID); + if (ruleCopy != null) { + return ruleCopy; + } else { + throw new IllegalStateException(); + } } /** @@ -252,7 +254,7 @@ public Rule add(Rule rule) { * @param rule candidate for unique ID * @return a rule with UID */ - protected Rule initRuleId(String rUID, Rule rule) { + protected @NonNull Rule initRuleId(String rUID, Rule rule) { Rule ruleWithUID = new Rule(rUID, rule.getTriggers(), rule.getConditions(), rule.getActions(), rule.getConfigurationDescriptions(), rule.getConfiguration(), rule.getTemplateUID(), rule.getVisibility()); @@ -380,7 +382,7 @@ public Rule get(String key) { @Override public Stream stream() { // create copies for consumers - return super.stream().map( r -> RuleUtils.getRuleCopy(r) ); + return super.stream().map(r -> RuleUtils.getRuleCopy(r)); } @Override @@ -587,9 +589,9 @@ public void runNow(String ruleUID) { ruleEngine.runNow(ruleUID); } - @Override - public void runNow(String ruleUID, boolean considerConditions, Map context) { - ruleEngine.runNow(ruleUID, considerConditions, context); - } + @Override + public void runNow(String ruleUID, boolean considerConditions, Map context) { + ruleEngine.runNow(ruleUID, considerConditions, context); + } } \ No newline at end of file diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/template/RuleTemplateRegistry.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/template/RuleTemplateRegistry.java index 4495f515973..8310809a567 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/template/RuleTemplateRegistry.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/template/RuleTemplateRegistry.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -63,11 +62,13 @@ public RuleTemplate get(String templateUID) { @Override public RuleTemplate get(String templateUID, Locale locale) { - RuleTemplate resultTemplate = null; for (Provider provider : elementMap.keySet()) { - resultTemplate = ((RuleTemplateProvider) provider).getTemplate(templateUID, locale); - if (resultTemplate != null) { - return createCopy(resultTemplate); + for (RuleTemplate resultTemplate : elementMap.get(provider)) { + if (resultTemplate.getUID().equals(templateUID)) { + RuleTemplate t = locale == null ? resultTemplate + : ((RuleTemplateProvider) provider).getTemplate(templateUID, locale); + return createCopy(t); + } } } return null; @@ -135,20 +136,15 @@ public Collection getByTag(String tag) { @Override public Collection getByTag(String tag, Locale locale) { Collection result = new ArrayList(20); - Collection templates = null; for (Provider provider : elementMap.keySet()) { - templates = ((RuleTemplateProvider) provider).getTemplates(locale); - if (templates != null) { - for (Iterator< RuleTemplate>it = templates.iterator(); it.hasNext();) { - RuleTemplate t = it.next(); - if (tag != null) { - Collection tags = t.getTags(); - if (tags != null && tags.contains(tag)) { - result.add(t); - } - } else { - result.add(t); - } + for (RuleTemplate resultTemplate : elementMap.get(provider)) { + Collection tags = resultTemplate.getTags(); + RuleTemplate t = locale == null ? resultTemplate + : ((RuleTemplateProvider) provider).getTemplate(resultTemplate.getUID(), locale); + if (tag == null) { + result.add(t); + } else if (tags != null && tags.contains(tag)) { + result.add(t); } } } @@ -164,22 +160,15 @@ public Collection getByTags(String... tags) { public Collection getByTags(Locale locale, String... tags) { Set tagSet = tags != null ? new HashSet(Arrays.asList(tags)) : null; Collection result = new ArrayList(20); - Collection templates = null; for (Provider provider : elementMap.keySet()) { - templates = ((RuleTemplateProvider) provider).getTemplates(locale); - if (templates != null) { - for (Iterator< RuleTemplate>it = templates.iterator(); it.hasNext();) { - RuleTemplate t = it.next(); - if (tagSet != null) { - Collection tTags = t.getTags(); - if (tTags != null) { - if (tTags.containsAll(tagSet)) { - result.add(t); - } - } - } else { - result.add(t); - } + for (RuleTemplate resultTemplate : elementMap.get(provider)) { + Collection tTags = resultTemplate.getTags(); + RuleTemplate t = locale == null ? resultTemplate + : ((RuleTemplateProvider) provider).getTemplate(resultTemplate.getUID(), locale); + if (tagSet == null) { + result.add(t); + } else if (tTags != null && tTags.containsAll(tagSet)) { + result.add(t); } } } @@ -190,4 +179,5 @@ public Collection getByTags(Locale locale, String... tags) { public Collection getAll(Locale locale) { return getByTag(null, locale); } + } diff --git a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/type/ModuleTypeRegistryImpl.java b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/type/ModuleTypeRegistryImpl.java index f314d900d93..cda10b2f953 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/type/ModuleTypeRegistryImpl.java +++ b/bundles/automation/org.eclipse.smarthome.automation.core/src/main/java/org/eclipse/smarthome/automation/core/internal/type/ModuleTypeRegistryImpl.java @@ -12,7 +12,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; @@ -61,13 +60,13 @@ public ModuleType get(String typeUID) { @Override @SuppressWarnings("unchecked") public T get(String moduleTypeUID, Locale locale) { - T mType = null; for (Provider provider : elementMap.keySet()) { - if (provider instanceof ModuleTypeProvider) { - mType = ((ModuleTypeProvider) provider).getModuleType(moduleTypeUID, locale); - } - if (mType != null) { - return (T) createCopy(mType); + for (ModuleType mType : elementMap.get(provider)) { + if (mType.getUID().equals(moduleTypeUID)) { + ModuleType mt = locale == null ? mType + : ((ModuleTypeProvider) provider).getModuleType(mType.getUID(), locale); + return (T) createCopy(mt); + } } } return null; @@ -82,20 +81,15 @@ public Collection getByTag(String moduleTypeTag) { @SuppressWarnings("unchecked") public Collection getByTag(String moduleTypeTag, Locale locale) { Collection result = new ArrayList(20); - Collection moduleTypes = null; for (Provider provider : elementMap.keySet()) { - moduleTypes = ((ModuleTypeProvider) provider).getModuleTypes(locale); - if (moduleTypes != null) { - for (Iterator it = moduleTypes.iterator(); it.hasNext();) { - ModuleType mt = it.next(); - if (moduleTypeTag != null) { - Collection tags = mt.getTags(); - if (tags != null && tags.contains(moduleTypeTag)) { - result.add((T) createCopy(mt)); - } - } else { - result.add((T) createCopy(mt)); - } + for (ModuleType mType : elementMap.get(provider)) { + ModuleType mt = locale == null ? mType + : ((ModuleTypeProvider) provider).getModuleType(mType.getUID(), locale); + Collection tags = mt.getTags(); + if (moduleTypeTag == null) { + result.add((T) createCopy(mt)); + } else if (tags != null && tags.contains(moduleTypeTag)) { + result.add((T) createCopy(mt)); } } } @@ -112,20 +106,14 @@ public Collection getByTags(String... tags) { public Collection getByTags(Locale locale, String... tags) { Set tagSet = tags != null ? new HashSet(Arrays.asList(tags)) : null; Collection result = new ArrayList(20); - Collection moduleTypes = null; for (Provider provider : elementMap.keySet()) { - moduleTypes = ((ModuleTypeProvider) provider).getModuleTypes(locale); - if (moduleTypes != null) { - for (Iterator it = moduleTypes.iterator(); it.hasNext();) { - ModuleType mt = it.next(); - if (tagSet != null) { - Collection mtTags = mt.getTags(); - if (mtTags.containsAll(tagSet)) { - result.add((T) createCopy(mt)); - } - } else { - result.add((T) createCopy(mt)); - } + for (ModuleType mType : elementMap.get(provider)) { + ModuleType mt = locale == null ? mType + : ((ModuleTypeProvider) provider).getModuleType(mType.getUID(), locale); + if (tagSet == null) { + result.add((T) createCopy(mt)); + } else if (mt.getTags().containsAll(tagSet)) { + result.add((T) createCopy(mt)); } } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/factory/CoreModuleHandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/factory/CoreModuleHandlerFactory.java index 12d31103422..26e7bf9fe8b 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/factory/CoreModuleHandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/factory/CoreModuleHandlerFactory.java @@ -185,7 +185,7 @@ protected synchronized ModuleHandler internalCreate(final Module module, final S } } - logger.error("The ModuleHandler is not supported:" + moduleTypeUID); + logger.error("The ModuleHandler is not supported:{}", moduleTypeUID); return null; } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/CompareConditionHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/CompareConditionHandler.java index db2060ab06b..26b8cca00e6 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/CompareConditionHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/CompareConditionHandler.java @@ -60,10 +60,10 @@ public boolean isSatisfied(Map context) { Object rightValue = getRightOperandValue(rightOperandString, toCompare); if (rightValue == null) { if (leftObj != null) { - logger.info("unsupported type for compare condition: " + leftObj.getClass()); + logger.info("unsupported type for compare condition: {}", leftObj.getClass()); } else { - logger.info("unsupported type for compare condition: null (" - + module.getInputs().get(INPUT_LEFT_FIELD) + ")"); + logger.info("unsupported type for compare condition: null ({})", + module.getInputs().get(INPUT_LEFT_FIELD)); } return false; } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/ItemStateConditionHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/ItemStateConditionHandler.java index 05b870da65b..fc4977c4417 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/ItemStateConditionHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.core/src/main/java/org/eclipse/smarthome/automation/module/core/handler/ItemStateConditionHandler.java @@ -89,11 +89,11 @@ public boolean isSatisfied(Map inputs) { Item item = itemRegistry.getItem(itemName); State compareState = TypeParser.parseState(item.getAcceptedDataTypes(), state); State itemState = item.getState(); - logger.debug("ItemStateCondition '" + module.getId() + "'checking if {} (State={}) {} {}", itemName, - itemState, operator, compareState); + logger.debug("ItemStateCondition '{}'checking if {} (State={}) {} {}", module.getId(), itemName, itemState, + operator, compareState); switch (operator) { case "=": - logger.debug("ConditionSatisfied --> " + itemState.equals(compareState)); + logger.debug("ConditionSatisfied --> {}", itemState.equals(compareState)); return itemState.equals(compareState); case "!=": return !itemState.equals(compareState); @@ -123,7 +123,7 @@ public boolean isSatisfied(Map inputs) { break; } } catch (ItemNotFoundException e) { - logger.error("Item with Name " + itemName + " not found in itemRegistry"); + logger.error("Item with Name {} not found in itemRegistry", itemName); return false; } return false; diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.media/src/main/java/org/eclipse/smarthome/automation/module/media/internal/MediaModuleHandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.media/src/main/java/org/eclipse/smarthome/automation/module/media/internal/MediaModuleHandlerFactory.java index 55ccf44eaed..33b419435eb 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.media/src/main/java/org/eclipse/smarthome/automation/module/media/internal/MediaModuleHandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.media/src/main/java/org/eclipse/smarthome/automation/module/media/internal/MediaModuleHandlerFactory.java @@ -21,6 +21,10 @@ import com.google.common.collect.ImmutableList; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class MediaModuleHandlerFactory extends BaseModuleHandlerFactory { private static final Collection types = ImmutableList.of(SayActionHandler.TYPE_ID, diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.defaultscope/src/main/java/org/eclipse/smarthome/automation/module/script/defaultscope/internal/ScriptBusEvent.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.defaultscope/src/main/java/org/eclipse/smarthome/automation/module/script/defaultscope/internal/ScriptBusEvent.java index fd588960a15..4655d4adfe7 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.defaultscope/src/main/java/org/eclipse/smarthome/automation/module/script/defaultscope/internal/ScriptBusEvent.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.defaultscope/src/main/java/org/eclipse/smarthome/automation/module/script/defaultscope/internal/ScriptBusEvent.java @@ -90,7 +90,7 @@ public Object sendCommand(String itemName, String commandString) { Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandString); eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, command)); } catch (ItemNotFoundException e) { - LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '" + itemName + "' does not exist."); + LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName); } } return null; @@ -150,7 +150,7 @@ public Object postUpdate(String itemName, String stateString) { State state = TypeParser.parseState(item.getAcceptedDataTypes(), stateString); eventPublisher.post(ItemEventFactory.createStateEvent(itemName, state)); } catch (ItemNotFoundException e) { - LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '" + itemName + "' does not exist."); + LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName); } } return null; diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/META-INF/MANIFEST.MF index 5aa47d3256e..589a437c26c 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/META-INF/MANIFEST.MF @@ -5,7 +5,8 @@ Bundle-SymbolicName: org.eclipse.smarthome.automation.module.script.rulesupport Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.smarthome.automation, +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.automation, org.eclipse.smarthome.automation.handler, org.eclipse.smarthome.automation.module.script, org.eclipse.smarthome.automation.module.script.rulesupport.shared, diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/internal/loader/ScriptFileWatcher.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/internal/loader/ScriptFileWatcher.java index 94070e97ae2..4a412699b19 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/internal/loader/ScriptFileWatcher.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/internal/loader/ScriptFileWatcher.java @@ -233,11 +233,8 @@ private void checkFiles() { @Override public int compare(URL o1, URL o2) { String f1 = o1.getPath(); - String s1 = f1.substring(f1.lastIndexOf("/") + 1); String f2 = o2.getPath(); - String s2 = f2.substring(f2.lastIndexOf("/") + 1); - - return String.CASE_INSENSITIVE_ORDER.compare(s1, s2); + return String.CASE_INSENSITIVE_ORDER.compare(f1, f2); } }); diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedActionHandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedActionHandlerFactory.java index 862abd3a420..85bf556712c 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedActionHandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedActionHandlerFactory.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.handler.ActionHandler; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface ScriptedActionHandlerFactory extends ScriptedHandler { public ActionHandler get(Action action); } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedConditionHandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedConditionHandlerFactory.java index 4e32e80ef4d..5eca08f24c3 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedConditionHandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedConditionHandlerFactory.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.handler.ConditionHandler; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface ScriptedConditionHandlerFactory extends ScriptedHandler { public ConditionHandler get(Condition module); } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedTriggerHandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedTriggerHandlerFactory.java index 6bd4f056a94..f5e5458d0af 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedTriggerHandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/factories/ScriptedTriggerHandlerFactory.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.handler.TriggerHandler; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface ScriptedTriggerHandlerFactory extends ScriptedHandler { public TriggerHandler get(Trigger module); } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleActionHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleActionHandler.java index 8b61efb748b..010ef33ac94 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleActionHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleActionHandler.java @@ -12,6 +12,10 @@ import org.eclipse.smarthome.automation.Action; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public abstract class SimpleActionHandler implements ScriptedHandler { public void init(Action module) { } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleConditionHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleConditionHandler.java index 6ce84b8be85..11047e7acaf 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleConditionHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleConditionHandler.java @@ -12,6 +12,10 @@ import org.eclipse.smarthome.automation.Condition; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public abstract class SimpleConditionHandler implements ScriptedHandler { public void init(Condition condition) { } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandler.java index a17cafdbd74..1c67fc332e6 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandler.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.Action; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface SimpleRuleActionHandler { Object execute(Action module, Map inputs); } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandlerDelegate.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandlerDelegate.java index e0d2ef889b4..ab956783d6b 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandlerDelegate.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleActionHandlerDelegate.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.Action; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public class SimpleRuleActionHandlerDelegate extends SimpleActionHandler { private SimpleRuleActionHandler handler; diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleEngineCallback.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleEngineCallback.java index 7e6952f599c..e9861fe0210 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleEngineCallback.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleRuleEngineCallback.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.automation.handler.RuleEngineCallback; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface SimpleRuleEngineCallback extends RuleEngineCallback { public void triggered(Map context); } diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleTriggerHandler.java b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleTriggerHandler.java index d8595aeb912..508ff1ab311 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleTriggerHandler.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.rulesupport/src/main/java/org/eclipse/smarthome/automation/module/script/rulesupport/shared/simple/SimpleTriggerHandler.java @@ -12,6 +12,10 @@ import org.eclipse.smarthome.automation.Trigger; import org.eclipse.smarthome.automation.module.script.rulesupport.shared.ScriptedHandler; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public abstract class SimpleTriggerHandler implements ScriptedHandler { private SimpleRuleEngineCallback ruleCallback; diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineContainer.java b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineContainer.java index 93488eb8c19..9288f7ac2fd 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineContainer.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineContainer.java @@ -9,6 +9,10 @@ import javax.script.ScriptEngine; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public class ScriptEngineContainer { private ScriptEngine scriptEngine; private ScriptEngineFactory factory; diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineManager.java b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineManager.java index cbb89d478d8..cff6e7b25c0 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineManager.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/ScriptEngineManager.java @@ -9,6 +9,10 @@ import java.io.InputStreamReader; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public interface ScriptEngineManager { /** diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/GenericScriptEngineFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/GenericScriptEngineFactory.java index 4a9b275ea4f..198775d6083 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/GenericScriptEngineFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/GenericScriptEngineFactory.java @@ -19,6 +19,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public class GenericScriptEngineFactory implements ScriptEngineFactory { private ScriptEngineManager engineManager = new ScriptEngineManager(); private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/NashornScriptEngineFactory.java b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/NashornScriptEngineFactory.java index cfae8f791db..557599364fc 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/NashornScriptEngineFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/NashornScriptEngineFactory.java @@ -22,6 +22,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public class NashornScriptEngineFactory implements ScriptEngineFactory { private final Logger logger = LoggerFactory.getLogger(this.getClass()); diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/ScriptExtensionManagerWrapper.java b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/ScriptExtensionManagerWrapper.java index b16addc69c5..4cfdedd0b02 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/ScriptExtensionManagerWrapper.java +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script/src/main/java/org/eclipse/smarthome/automation/module/script/internal/ScriptExtensionManagerWrapper.java @@ -12,6 +12,10 @@ import org.eclipse.smarthome.automation.module.script.ScriptEngineContainer; import org.eclipse.smarthome.automation.module.script.ScriptExtensionProvider; +/** + * + * @author Simon Merschjohann - Initial contribution + */ public class ScriptExtensionManagerWrapper { private ScriptEngineContainer container; diff --git a/bundles/automation/org.eclipse.smarthome.automation.provider.file/src/main/java/org/eclipse/smarthome/automation/internal/provider/file/AbstractFileProvider.java b/bundles/automation/org.eclipse.smarthome.automation.provider.file/src/main/java/org/eclipse/smarthome/automation/internal/provider/file/AbstractFileProvider.java index 8bf20dfb489..7ccdccd23bb 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.provider.file/src/main/java/org/eclipse/smarthome/automation/internal/provider/file/AbstractFileProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.provider.file/src/main/java/org/eclipse/smarthome/automation/internal/provider/file/AbstractFileProvider.java @@ -225,9 +225,9 @@ protected void importFile(String parserType, URL url) { Set providedObjects = parser.parse(inputStreamReader); updateProvidedObjectsHolder(url, providedObjects); } catch (ParsingException e) { - logger.debug(e.getMessage(), e); + logger.debug("{}", e.getMessage(), e); } catch (IOException e) { - logger.debug(e.getMessage(), e); + logger.debug("{}", e.getMessage(), e); } finally { if (inputStreamReader != null) { try { diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/ModuleTypeProvider.xml b/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/ModuleTypeProvider.xml index 99de34b5750..53b4c78636d 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/ModuleTypeProvider.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/ModuleTypeProvider.xml @@ -16,7 +16,6 @@ - diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/TemplateProvider.xml b/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/TemplateProvider.xml index 27638ca534c..ddb44e62e20 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/TemplateProvider.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/OSGI-INF/TemplateProvider.xml @@ -16,7 +16,6 @@ - diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/AbstractResourceBundleProvider.java b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/AbstractResourceBundleProvider.java index 014f325a5be..9a6da3f6816 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/AbstractResourceBundleProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/AbstractResourceBundleProvider.java @@ -14,6 +14,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Enumeration; import java.util.LinkedList; @@ -459,10 +460,10 @@ protected Set parseData(Parser parser, URL url, Bundle bundle) { InputStream is = null; try { is = url.openStream(); - reader = new InputStreamReader(is); + reader = new InputStreamReader(is, StandardCharsets.UTF_8); return parser.parse(reader); } catch (ParsingException e) { - logger.error(e.getLocalizedMessage(), e); + logger.error("{}", e.getLocalizedMessage(), e); } catch (IOException e) { logger.error("Can't read from resource of bundle with ID {}", bundle.getBundleId(), e); processAutomationProviderUninstalled(bundle); @@ -486,29 +487,25 @@ protected Set parseData(Parser parser, URL url, Bundle bundle) { @SuppressWarnings("unchecked") protected void addNewProvidedObjects(List newPortfolio, List previousPortfolio, Set parsedObjects) { + List> snapshot = null; + synchronized (listeners) { + snapshot = new LinkedList>(listeners); + } for (E parsedObject : parsedObjects) { String uid = getUID(parsedObject); - if (providedObjectsHolder.get(uid) == null) { - if (checkExistence(uid)) { - continue; - } - } else if (previousPortfolio == null || !previousPortfolio.contains(uid)) { - logger.error("{} with UID \"{}\" already exists! Failed to create a second with the same UID!", - parsedObject.getClass().getName(), uid, new IllegalArgumentException()); + E oldElement = providedObjectsHolder.get(uid); + if (oldElement != null && !previousPortfolio.contains(uid)) { + logger.warn("{} with UID '{}' already exists! Failed to add a second with the same UID!", + parsedObject.getClass().getName(), uid); continue; - } - newPortfolio.add(uid); - E oldelement = providedObjectsHolder.put(uid, parsedObject); - if (listeners != null) { - List> snapshot = null; - synchronized (listeners) { - snapshot = new LinkedList>(listeners); - } + } else { + newPortfolio.add(uid); + providedObjectsHolder.put(uid, parsedObject); for (ProviderChangeListener listener : snapshot) { - if (oldelement == null) { + if (oldElement == null) { listener.added((Provider) this, parsedObject); } else { - listener.updated((Provider) this, oldelement, parsedObject); + listener.updated((Provider) this, oldElement, parsedObject); } } } @@ -530,12 +527,6 @@ protected void updateWaitingProviders(Parser parser, Bundle bundle, URL url) } } - /** - * @param uid - * @return - */ - protected abstract boolean checkExistence(String uid); - /** * @param parsedObject * @return diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/ModuleTypeResourceBundleProvider.java b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/ModuleTypeResourceBundleProvider.java index c6c37303dc7..32dce8836d6 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/ModuleTypeResourceBundleProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/ModuleTypeResourceBundleProvider.java @@ -52,8 +52,6 @@ public class ModuleTypeResourceBundleProvider extends AbstractResourceBundleProvider implements ModuleTypeProvider { - protected ModuleTypeRegistry moduleTypeRegistry; - /** * This constructor is responsible for initializing the path to resources and tracking the * {@link ModuleTypeRegistry}. @@ -105,31 +103,6 @@ public Collection getModuleTypes(Locale locale) { return moduleTypesList; } - protected void setModuleTypeRegistry(ModuleTypeRegistry moduleTypeRegistry) { - this.moduleTypeRegistry = moduleTypeRegistry; - } - - protected void removeModuleTypeRegistry(ModuleTypeRegistry moduleTypeRegistry) { - this.moduleTypeRegistry = null; - } - - /** - * This method is responsible for checking the existence of {@link ModuleType}s with the same UIDs before these - * objects to be added in the system. - * - * @param uid UID of the newly created {@link ModuleType}, which to be checked. - * @return {@code true} if {@link ModuleType} with the same UID exists or {@code false} in the opposite case. - */ - @Override - protected boolean checkExistence(String uid) { - if (moduleTypeRegistry.get(uid) != null) { - logger.error("Module Type with UID \"{}\" already exists! Failed to create a second with the same UID!", - uid, new IllegalArgumentException()); - return true; - } - return false; - } - @Override protected String getUID(ModuleType parsedObject) { return parsedObject.getUID(); diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/RuleResourceBundleImporter.java b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/RuleResourceBundleImporter.java index cde91018ebe..a022016f8b7 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/RuleResourceBundleImporter.java +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/RuleResourceBundleImporter.java @@ -153,11 +153,6 @@ protected void processAutomationProviderUninstalled(Bundle bundle) { providerPortfolio.remove(vendor); } - @Override - protected boolean checkExistence(String uid) { - return mProvider.get(uid) != null; - } - @Override protected String getUID(Rule parsedObject) { return parsedObject.getUID(); diff --git a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/TemplateResourceBundleProvider.java b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/TemplateResourceBundleProvider.java index b9c5edb0752..0884d8ae8e4 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/TemplateResourceBundleProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.providers/src/main/java/org/eclipse/smarthome/automation/internal/core/provider/TemplateResourceBundleProvider.java @@ -23,7 +23,6 @@ import org.eclipse.smarthome.automation.template.RuleTemplateProvider; import org.eclipse.smarthome.automation.template.Template; import org.eclipse.smarthome.automation.template.TemplateProvider; -import org.eclipse.smarthome.automation.template.TemplateRegistry; import org.eclipse.smarthome.automation.type.ModuleType; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.core.common.registry.ProviderChangeListener; @@ -48,8 +47,6 @@ public class TemplateResourceBundleProvider extends AbstractResourceBundleProvider implements RuleTemplateProvider { - protected TemplateRegistry templateRegistry; - /** * This constructor is responsible for initializing the path to resources and tracking the managing service of the * {@link ModuleType}s and the managing service of the {@link RuleTemplates}s. @@ -100,32 +97,6 @@ public Collection getTemplates(Locale locale) { return templatesList; } - protected void setTemplateRegistry(TemplateRegistry templateRegistry) { - this.templateRegistry = templateRegistry; - } - - protected void removeTemplateRegistry(TemplateRegistry templateRegistry) { - this.templateRegistry = null; - } - - /** - * This method is responsible for checking the existence of {@link ModuleType}s or {@link Template}s with the same - * UIDs before these objects to be added in the system. - * - * @param uid UID of the newly created {@link Template}, which to be checked. - * @return {@code true} if {@link Template} with the same UID exists or {@code false} in the opposite - * case. - */ - @Override - protected boolean checkExistence(String uid) { - if (templateRegistry != null && templateRegistry.get(uid) != null) { - logger.error("Rule Template with UID \"{}\" already exists! Failed to create a second with the same UID!", - uid, new IllegalArgumentException()); - return true; - } - return false; - } - /** * This method is used to localize the {@link Template}s. * diff --git a/bundles/automation/org.eclipse.smarthome.automation.rest/src/main/java/org/eclipse/smarthome/automation/rest/internal/RuleResource.java b/bundles/automation/org.eclipse.smarthome.automation.rest/src/main/java/org/eclipse/smarthome/automation/rest/internal/RuleResource.java index b34cd973a84..8870023ecc4 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.rest/src/main/java/org/eclipse/smarthome/automation/rest/internal/RuleResource.java +++ b/bundles/automation/org.eclipse.smarthome.automation.rest/src/main/java/org/eclipse/smarthome/automation/rest/internal/RuleResource.java @@ -133,12 +133,12 @@ public Response create(@ApiParam(value = "rule data", required = true) RuleDTO r } catch (IllegalArgumentException e) { String errMessage = "Creation of the rule is refused: " + e.getMessage(); - logger.warn(errMessage); + logger.warn("{}", errMessage); return JSONResponse.createErrorResponse(Status.CONFLICT, errMessage); } catch (RuntimeException e) { String errMessage = "Creation of the rule is refused: " + e.getMessage(); - logger.warn(errMessage); + logger.warn("{}", errMessage); return JSONResponse.createErrorResponse(Status.BAD_REQUEST, errMessage); } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/WelcomeHomeCommands.java b/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/WelcomeHomeCommands.java index 3f3dae1fa5b..4ad2536b145 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/WelcomeHomeCommands.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/WelcomeHomeCommands.java @@ -82,8 +82,8 @@ public void execute(String[] args, Console console) { } if (COMMAND_SETTINGS.equalsIgnoreCase(command) || COMMAND_SETTINGS_SHORT.equalsIgnoreCase(command)) { settings(params, console); - } else - if (COMMAND_ACTIVATE_AC.equalsIgnoreCase(command) || COMMAND_ACTIVATE_AC_SHORT.equalsIgnoreCase(command)) { + } else if (COMMAND_ACTIVATE_AC.equalsIgnoreCase(command) + || COMMAND_ACTIVATE_AC_SHORT.equalsIgnoreCase(command)) { activate(params, console); } else if (COMMAND_ACTIVATE_L.equalsIgnoreCase(command) || COMMAND_ACTIVATE_L_SHORT.equalsIgnoreCase(command)) { activateLights(params, console); @@ -212,7 +212,6 @@ private void settings(String[] params, Console console) { console.println("Missing required parameters"); return; } - @SuppressWarnings("unchecked") Configuration config = rulesProvider.rules.get(WelcomeHomeRulesProvider.AC_UID).getConfiguration(); if (params[0] != null && (params[0].equalsIgnoreCase(TemperatureConditionType.OPERATOR_HEATING) || params[0].equalsIgnoreCase(TemperatureConditionType.OPERATOR_COOLING))) { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/type/WelcomeHomeModuleTypeProvider.java b/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/type/WelcomeHomeModuleTypeProvider.java index 564d4c2aff1..64001b63713 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/type/WelcomeHomeModuleTypeProvider.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/type/WelcomeHomeModuleTypeProvider.java @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Hashtable; import java.util.Locale; import java.util.Map; @@ -89,7 +88,7 @@ public Collection getAll() { @Override public void removeProviderChangeListener(ProviderChangeListener listener) { - // does nothing because this provider does not change + // does nothing because this provider does not change } } diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.java.demo/src/main/java/org/eclipse/smarthome/automation/sample/java/demo/SampleJavaDemo.java b/bundles/automation/org.eclipse.smarthome.automation.sample.java.demo/src/main/java/org/eclipse/smarthome/automation/sample/java/demo/SampleJavaDemo.java index 0ee86f606b6..e3460c22f7f 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.java.demo/src/main/java/org/eclipse/smarthome/automation/sample/java/demo/SampleJavaDemo.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.java.demo/src/main/java/org/eclipse/smarthome/automation/sample/java/demo/SampleJavaDemo.java @@ -29,6 +29,8 @@ /** * This class shows how to create a rule, using the Java API.It also shows how to add it to the rule engine via * RuleRegistry interface. + * + * @author Plamen Peev - Initial contribution */ public class SampleJavaDemo { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/META-INF/MANIFEST.MF index 5e6fdce20f5..cdde111bca9 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/META-INF/MANIFEST.MF @@ -4,15 +4,15 @@ Bundle-Name: Eclipse SmartHome Automation Module Type Demo Bundle-SymbolicName: org.eclipse.smarthome.automation.sample.moduletype.demo Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Bosch Software Innovations GmbH -Import-Package: org.eclipse.smarthome.automation.handler, +Import-Package: org.eclipse.smarthome.automation, + org.eclipse.smarthome.automation.handler, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.events, org.eclipse.smarthome.io.console, org.eclipse.smarthome.io.console.extensions, org.osgi.framework, org.osgi.service.component, + org.osgi.service.event, org.osgi.util.tracker, org.slf4j Service-Component: OSGI-INF/*.xml -Require-Bundle: org.eclipse.smarthome.automation.api, - org.eclipse.osgi.services diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommand.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommand.java index c7d6147ea6b..cc35a6d3bdd 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommand.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommand.java @@ -19,6 +19,8 @@ /** * This class is base for the commands in this demo automation commands. It defines common functionality for the * commands. + * + * @author Plamen Peev - Initial contribution */ public abstract class DemoCommand { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommandsPluggable.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommandsPluggable.java index d2f9e456f4c..baca20d0328 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommandsPluggable.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/DemoCommandsPluggable.java @@ -27,6 +27,8 @@ /** * This class provides functionality for defining and executing automation commands for importing, exporting, removing * and listing the automation objects. + * + * @author Plamen Peev - Initial contribution */ public class DemoCommandsPluggable extends AbstractConsoleCommandExtension { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/PostEventCommand.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/PostEventCommand.java index 2f50a78ea74..7109ef304a9 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/PostEventCommand.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/commands/PostEventCommand.java @@ -32,6 +32,8 @@ * The event will be with topic: test/demo/topic * and entry with key: test and value 10 * + * + * @author Plamen Peev - Initial contribution */ public class PostEventCommand extends DemoCommand { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/factory/HandlerFactory.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/factory/HandlerFactory.java index 6e6e5ed57fd..6ef49369687 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/factory/HandlerFactory.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/factory/HandlerFactory.java @@ -38,6 +38,8 @@ /** * This class is a factory for creating {@link ConsoleTrigger}, {@link CompareCondition} and {@link ConsolePrintAction} * objects. + * + * @author Plamen Peev - Initial contribution */ public class HandlerFactory extends BaseModuleHandlerFactory implements ModuleHandlerFactory { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/CompareCondition.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/CompareCondition.java index 8e9e37db2c1..5142a5e7ff4 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/CompareCondition.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/CompareCondition.java @@ -46,6 +46,8 @@ *
  * constraint operator inputValue
  * 
+ * + * @author Plamen Peev - Initial contribution */ public class CompareCondition extends BaseModuleHandler implements ConditionHandler { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsolePrintAction.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsolePrintAction.java index 26fb452ca82..10062823072 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsolePrintAction.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsolePrintAction.java @@ -38,6 +38,8 @@ * * * This handler prints to standard output {@link Action}'s typeUID + the UID of the rule for which is created. + * + * @author Plamen Peev - Initial contribution */ public class ConsolePrintAction extends BaseModuleHandler implements ActionHandler { diff --git a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsoleTrigger.java b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsoleTrigger.java index 37ceb4fb66f..50a5931d157 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsoleTrigger.java +++ b/bundles/automation/org.eclipse.smarthome.automation.sample.moduletype.demo/src/main/java/org/eclipse/smarthome/automation/sample/moduletype/handlers/ConsoleTrigger.java @@ -47,6 +47,8 @@ * "keyName":"key" * } * + * + * @author Plamen Peev - Initial contribution */ public class ConsoleTrigger extends BaseModuleHandler implements TriggerHandler, EventHandler { diff --git a/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units.properties b/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units.properties index c0430cfaa9b..a4474845a3c 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units.properties +++ b/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units.properties @@ -4,8 +4,10 @@ unit.m/s2=m/s\u00B2 unit.m2=m\u00B2 unit.m3=m\u00B3 unit.kph=km/h +unit.ms=milliseconds unit.min=minutes unit.h=hours unit.d=days unit.week=weeks -unit.y=years \ No newline at end of file +unit.y=years + diff --git a/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units_de.properties b/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units_de.properties index 91f4791691d..ecffc3bf8b3 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units_de.properties +++ b/bundles/config/org.eclipse.smarthome.config.core/ESH-INF/i18n/units_de.properties @@ -1,6 +1,8 @@ unit.s=Sekunden +unit.ms=Millisekunden unit.min=Minuten unit.h=Stunden unit.d=Tage unit.week=Wochen -unit.y=Jahre \ No newline at end of file +unit.y=Jahre + diff --git a/bundles/config/org.eclipse.smarthome.config.core/META-INF/MANIFEST.MF b/bundles/config/org.eclipse.smarthome.config.core/META-INF/MANIFEST.MF index 7fa7da1ff2f..f2f7dd5ecce 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/META-INF/MANIFEST.MF +++ b/bundles/config/org.eclipse.smarthome.config.core/META-INF/MANIFEST.MF @@ -4,16 +4,24 @@ Bundle-Name: Eclipse SmartHome Config Core Bundle-SymbolicName: org.eclipse.smarthome.config.core Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.google.common.base, com.google.common.collect, com.google.gson, org.apache.commons.lang.reflect, + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.core.dto, + org.eclipse.smarthome.config.core.i18n, + org.eclipse.smarthome.config.core.status, + org.eclipse.smarthome.config.core.status.events, + org.eclipse.smarthome.config.core.validation, org.eclipse.smarthome.core.common, org.eclipse.smarthome.core.common.osgi, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.i18n, + org.eclipse.smarthome.core.net, org.osgi.framework, org.osgi.service.component, org.osgi.service.component.annotations;resolution:=optional, diff --git a/bundles/config/org.eclipse.smarthome.config.core/OSGI-INF/.gitignore b/bundles/config/org.eclipse.smarthome.config.core/OSGI-INF/.gitignore index 6ff7b9a7794..50d869a42cb 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/OSGI-INF/.gitignore +++ b/bundles/config/org.eclipse.smarthome.config.core/OSGI-INF/.gitignore @@ -1 +1,2 @@ /org.eclipse.smarthome.config.core.internal.i18n.I18nConfigOptionsProvider.xml +/org.eclipse.smarthome.config.core.net.internal.NetworkConfigOptionProvider.xml diff --git a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/ConfigDescriptionParameter.java b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/ConfigDescriptionParameter.java index 6c4623ef81b..786abc8c474 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/ConfigDescriptionParameter.java +++ b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/ConfigDescriptionParameter.java @@ -95,7 +95,7 @@ public enum Type { private static final Set UNITS = Collections .unmodifiableSet(new HashSet(Arrays.asList("A", "cd", "K", "kg", "m", "mol", "s", "g", "rad", "sr", "Hz", "N", "Pa", "J", "W", "C", "V", "F", "Ω", "S", "Wb", "T", "H", "Cel", "lm", "lx", "Bq", "Gy", - "Sv", "kat", "m/s2", "m2v", "m3", "kph", "%", "l", "min", "h", "d", "week", "y"))); + "Sv", "kat", "m/s2", "m2v", "m3", "kph", "%", "l", "ms", "min", "h", "d", "week", "y"))); /** * Default constructor. diff --git a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/Configuration.java b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/Configuration.java index 73076628070..2e3130a7ed1 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/Configuration.java +++ b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/Configuration.java @@ -60,7 +60,7 @@ public T as(Class configurationClass) { try { configuration = configurationClass.newInstance(); } catch (InstantiationException | IllegalAccessException ex) { - logger.error("Could not create configuration instance: " + ex.getMessage(), ex); + logger.error("Could not create configuration instance: {}", ex.getMessage(), ex); return null; } @@ -96,7 +96,7 @@ public T as(Class configurationClass) { FieldUtils.writeField(configuration, fieldName, value, true); } } catch (Exception ex) { - logger.warn("Could not set field value for field '" + fieldName + "': " + ex.getMessage(), ex); + logger.warn("Could not set field value for field '{}': {}", fieldName, ex.getMessage(), ex); } } diff --git a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/net/internal/NetworkConfigOptionProvider.java b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/net/internal/NetworkConfigOptionProvider.java new file mode 100644 index 00000000000..7d56ef0be6c --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/net/internal/NetworkConfigOptionProvider.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.config.core.net.internal; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.config.core.ConfigOptionProvider; +import org.eclipse.smarthome.config.core.ParameterOption; +import org.eclipse.smarthome.core.net.NetUtil; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides a list of IPv4 addresses of the local machine and shows the user which interface belongs to which IP address + * + * @author Stefan Triller - initial contribution + * + */ +@Component +public class NetworkConfigOptionProvider implements ConfigOptionProvider { + + static final URI CONFIG_URI = URI.create("system:network"); + static final String PARAM_PRIMARY_ADDRESS = "primaryAddress"; + + private final Logger logger = LoggerFactory.getLogger(NetworkConfigOptionProvider.class); + + @Override + public Collection getParameterOptions(URI uri, String param, Locale locale) { + if (!uri.equals(CONFIG_URI)) { + return null; + } + + if (param.equals(PARAM_PRIMARY_ADDRESS)) { + return getIPv4Addresses(); + } + return null; + } + + private List getIPv4Addresses() { + List interfaceOptions = new ArrayList<>(); + + Set subnets = new HashSet<>(); + + try { + final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + final NetworkInterface current = interfaces.nextElement(); + if (!current.isUp() || current.isLoopback() || current.isVirtual()) { + continue; + } + + for (InterfaceAddress ifAddr : current.getInterfaceAddresses()) { + InetAddress addr = ifAddr.getAddress(); + + if (addr.isLoopbackAddress() || (addr instanceof Inet6Address)) { + continue; + } + + @SuppressWarnings("null") + @NonNull + String ipv4Address = addr.getHostAddress(); + try { + String subNetString = NetUtil.getIpv4NetAddress(ipv4Address, ifAddr.getNetworkPrefixLength()) + + "/" + String.valueOf(ifAddr.getNetworkPrefixLength()); + subnets.add(subNetString); + } catch (IllegalArgumentException ex) { + logger.error("Could not calculate network address: {} Ignoring IP {}", ex.getMessage(), + ipv4Address, ex); + } + } + } + } catch (SocketException ex) { + logger.error("Could not retrieve network interface: {}", ex.getMessage(), ex); + return null; + } + + for (String subnet : subnets) { + ParameterOption po = new ParameterOption(subnet, subnet); + interfaceOptions.add(po); + } + + return interfaceOptions; + } + +} diff --git a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusService.java b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusService.java index d994ae3114d..60e209de532 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusService.java +++ b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusService.java @@ -16,8 +16,8 @@ import org.eclipse.smarthome.config.core.status.events.ConfigStatusInfoEvent; import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; @@ -87,8 +87,8 @@ public void run() { if (eventPublisher != null) { eventPublisher.post(new ConfigStatusInfoEvent(configStatusSource.getTopic(), info)); } else { - logger.warn("EventPublisher not available. Cannot post new config status for entity " - + configStatusSource.entityId); + logger.warn("EventPublisher not available. Cannot post new config status for entity {}", + configStatusSource.entityId); } } } diff --git a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusSource.java b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusSource.java index 86a18958265..08b2cf6965d 100644 --- a/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusSource.java +++ b/bundles/config/org.eclipse.smarthome.config.core/src/main/java/org/eclipse/smarthome/config/core/status/ConfigStatusSource.java @@ -7,7 +7,7 @@ */ package org.eclipse.smarthome.config.core.status; -import com.google.common.base.Preconditions; +import org.eclipse.jdt.annotation.NonNull; /** * The {@link ConfigStatusSource} represents a source which would like to propagate its new configuration status. It is @@ -24,12 +24,9 @@ public abstract class ConfigStatusSource { * Creates a new config status source object. * * @param entityId the id of the entity whose new configuration status is to be propagated - * - * @throws NullPointerException if given entity id is null */ - public ConfigStatusSource(String entityId) { + public ConfigStatusSource(@NonNull String entityId) { super(); - Preconditions.checkNotNull(entityId); this.entityId = entityId; } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/META-INF/MANIFEST.MF b/bundles/config/org.eclipse.smarthome.config.discovery.test/META-INF/MANIFEST.MF index 76448acf7b3..e88c15fc3fd 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery.test/META-INF/MANIFEST.MF +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/META-INF/MANIFEST.MF @@ -14,11 +14,13 @@ Import-Package: com.google.gson, org.codehaus.groovy.runtime.typehandling, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.storage, + org.eclipse.smarthome.core.thing.type, org.eclipse.smarthome.core.thing.binding, org.eclipse.smarthome.core.thing.binding.builder, org.eclipse.smarthome.core.types, org.eclipse.smarthome.test, org.eclipse.smarthome.test.java, + org.eclipse.smarthome.test.storage, org.hamcrest;core=split, org.junit;version="4.0.0", org.mockito, diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/org.eclipse.smarthome.config.discovery.test.launch b/bundles/config/org.eclipse.smarthome.config.discovery.test/org.eclipse.smarthome.config.discovery.test.launch index b8c7dc9b331..467eaddb491 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery.test/org.eclipse.smarthome.config.discovery.test.launch +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/org.eclipse.smarthome.config.discovery.test.launch @@ -26,7 +26,7 @@ - + @@ -34,8 +34,8 @@ - - + + diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/discovery/DiscoveryResultImplTest.groovy b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/discovery/DiscoveryResultImplTest.groovy index 66ae66d0ece..cde1294e0ae 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/discovery/DiscoveryResultImplTest.groovy +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/discovery/DiscoveryResultImplTest.groovy @@ -19,7 +19,7 @@ import org.junit.Test /** * The {@link DiscoveryResultTest} checks if any invalid input parameters * and the synchronization of {@link DiscoveryResult}s work in a correct way. - * + * * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added representation */ diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/DynamicThingUpdateOSGITest.groovy b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/DynamicThingUpdateOSGITest.groovy index 6e122e7b85c..59c8bc66ff7 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/DynamicThingUpdateOSGITest.groovy +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/DynamicThingUpdateOSGITest.groovy @@ -25,8 +25,10 @@ import org.eclipse.smarthome.core.thing.ThingUID import org.eclipse.smarthome.core.thing.binding.ThingHandler import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory +import org.eclipse.smarthome.core.thing.binding.ThingTypeProvider import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder import org.eclipse.smarthome.core.thing.binding.builder.ThingStatusInfoBuilder +import org.eclipse.smarthome.core.thing.type.ThingType import org.eclipse.smarthome.core.types.Command import org.eclipse.smarthome.core.types.State import org.eclipse.smarthome.test.OSGiTest @@ -47,6 +49,7 @@ import org.junit.Test * * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added representation + * @author Andre Fuechsel - Added tests for device id */ class DynamicThingUpdateOSGITest extends OSGiTest { @@ -55,9 +58,14 @@ class DynamicThingUpdateOSGITest extends OSGiTest { final BINDING_ID = 'dynamicUpdateBindingId' final THING_TYPE_ID = 'dynamicUpdateThingType' final THING_ID = 'dynamicUpdateThingId' + final THING_ID2 = 'dynamicUpdateThingId2' + final DEVICE_ID = 'deviceId' + final DEVICE_ID_KEY = 'deviceIdKey' final ThingTypeUID THING_TYPE_UID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID) final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, THING_ID) + final ThingUID THING_UID2 = new ThingUID(THING_TYPE_UID, THING_ID2) + final ThingType THING_TYPE = new ThingType(THING_TYPE_UID, null, "label", null, true, DEVICE_ID_KEY, null, null, null, null); Inbox inbox DiscoveryServiceRegistry discoveryServiceRegistry @@ -74,6 +82,9 @@ class DynamicThingUpdateOSGITest extends OSGiTest { void setUp() { registerVolatileStorageService() + def thingTypeProvider = [ "getThingType" : { uid, locale -> THING_TYPE } ] as ThingTypeProvider + registerService(thingTypeProvider) + inbox = getService Inbox discoveryServiceRegistry = getService DiscoveryServiceRegistry managedThingProvider = getService ManagedThingProvider @@ -87,6 +98,8 @@ class DynamicThingUpdateOSGITest extends OSGiTest { managedThingProvider.all.each { managedThingProvider.remove(it.getUID()) } + + unregisterMocks() } ThingHandler createThingHandler(Thing thing) { @@ -102,7 +115,7 @@ class DynamicThingUpdateOSGITest extends OSGiTest { this.thingUpdated = true this.updatedThing = updatedThing }, - setCallback: {callbackArg -> callback = callbackArg }, + 'setCallback' : { callbackArg -> callback = callbackArg } ] as ThingHandler ) return thingHandler @@ -179,4 +192,22 @@ class DynamicThingUpdateOSGITest extends OSGiTest { unregisterService(thingHandlerFactory) } + + @Test + void 'assert that an thing with different thing uid as the already existing thing is added'() { + assertThat inbox.getAll().size(), is(0) + + ThingHandlerFactory thingHandlerFactory = createThingHandlerFactory() + registerService(thingHandlerFactory, ThingHandlerFactory.class.name) + + managedThingProvider.add ThingBuilder.create(THING_TYPE_UID, THING_ID).build() + + DiscoveryResult discoveryResult = new DiscoveryResultImpl(THING_UID2, null, [:], null, "DummyLabel", DEFAULT_TTL) + + inbox.add discoveryResult + + assertThat inbox.getAll().size(), is(1) + + unregisterService(thingHandlerFactory) + } } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/InboxOSGITest.groovy b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/InboxOSGITest.groovy index 0283776c1ea..15125098478 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/InboxOSGITest.groovy +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/groovy/org/eclipse/smarthome/config/setup/test/inbox/InboxOSGITest.groovy @@ -98,10 +98,26 @@ class InboxOSGITest extends OSGiTest { ] final DiscoveryResult testDiscoveryResult = DiscoveryResultBuilder.create(testThing.getUID()).withProperties(discoveryResultProperties).withLabel(discoveryResultLabel).build() final ThingType testThingType = new ThingType(testTypeUID, null, "label", "", null, null, null, testURI) - final ConfigDescriptionParameter[] configDescriptionParameter = [[discoveryResultProperties.keySet().getAt(0), Type.TEXT], [discoveryResultProperties.keySet().getAt(1), Type.INTEGER]] + final ConfigDescriptionParameter[] configDescriptionParameter = [ + [ + discoveryResultProperties.keySet().getAt(0), + Type.TEXT + ], + [ + discoveryResultProperties.keySet().getAt(1), + Type.INTEGER + ] + ] final ConfigDescription testConfigDescription = new ConfigDescription(testURI, Arrays.asList(configDescriptionParameter)) - final String[] keysInConfigDescription = [discoveryResultProperties.keySet().getAt(0), discoveryResultProperties.keySet().getAt(1)] - final String[] keysNotInConfigDescription = [discoveryResultProperties.keySet().getAt(2), discoveryResultProperties.keySet().getAt(3), discoveryResultProperties.keySet().getAt(4)] + final String[] keysInConfigDescription = [ + discoveryResultProperties.keySet().getAt(0), + discoveryResultProperties.keySet().getAt(1) + ] + final String[] keysNotInConfigDescription = [ + discoveryResultProperties.keySet().getAt(2), + discoveryResultProperties.keySet().getAt(3), + discoveryResultProperties.keySet().getAt(4) + ] final Map discoveryResults = [:] final List inboxListeners = new ArrayList<>() @@ -354,23 +370,46 @@ class InboxOSGITest extends OSGiTest { assertThat allDiscoveryResults.size(), is(4) List discoveryResults = inbox.get(null) - assertIncludesAll([discoveryResult1, discoveryResult2, discoveryResult3, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult2, + discoveryResult3, + discoveryResult4 + ], discoveryResults) // Filter by nothing discoveryResults = inbox.get(new InboxFilterCriteria(null, null)) - assertIncludesAll([discoveryResult1, discoveryResult2, discoveryResult3, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult2, + discoveryResult3, + discoveryResult4 + ], discoveryResults) // Filter by thingType discoveryResults = inbox.get(new InboxFilterCriteria(thingTypeUID, null)) - assertIncludesAll([discoveryResult1, discoveryResult2, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult2, + discoveryResult4 + ], discoveryResults) // Filter by bindingId discoveryResults = inbox.get(new InboxFilterCriteria("dummyBindingId", null)) - assertIncludesAll([discoveryResult1, discoveryResult2, discoveryResult3, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult2, + discoveryResult3, + discoveryResult4 + ], discoveryResults) // Filter by DiscoveryResultFlag discoveryResults = inbox.get(new InboxFilterCriteria((String)null, DiscoveryResultFlag.NEW)) - assertIncludesAll([discoveryResult1, discoveryResult3, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult3, + discoveryResult4 + ], discoveryResults) // Filter by thingId discoveryResults = inbox.get(new InboxFilterCriteria(new ThingUID(thingTypeUID, "dummyThingId4"), null)) @@ -382,7 +421,11 @@ class InboxOSGITest extends OSGiTest { // Filter by bindingId and DiscoveryResultFlag discoveryResults = inbox.get(new InboxFilterCriteria("dummyBindingId", DiscoveryResultFlag.NEW)) - assertIncludesAll([discoveryResult1, discoveryResult3, discoveryResult4], discoveryResults) + assertIncludesAll([ + discoveryResult1, + discoveryResult3, + discoveryResult4 + ], discoveryResults) } @Test @@ -609,6 +652,26 @@ class InboxOSGITest extends OSGiTest { assertThat inbox.getAll().size(), is(0) } + @Test + void 'assert that DiscoveryResult is added to Inbox when thing with different UID exists'() { + assertThat inbox.getAll().size(), is(0) + + ThingTypeUID thingTypeUID = new ThingTypeUID("dummyBindingId2", "dummyThingType") + ThingUID thingUID = new ThingUID(thingTypeUID, "dummyThingId") + + managedThingProvider.add ThingBuilder.create(thingTypeUID, "dummyThingId").build() + + Map props = new HashMap<>() + props.put("property1", "property1value1") + props.put("property2", "property2value1") + + DiscoveryResult discoveryResult = new DiscoveryResultImpl(thingUID, null, null, null, "DummyLabel1", DEFAULT_TTL) + + inbox.add discoveryResult + + assertThat inbox.getAll().size(), is(0) + } + void assertIncludesAll(List expectedList, List actualList) { assertThat actualList.size(), is (expectedList.size()) expectedList.each { diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicatesTest.java b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicatesTest.java new file mode 100644 index 00000000000..7b403ff0a34 --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicatesTest.java @@ -0,0 +1,160 @@ +package org.eclipse.smarthome.config.discovery.inbox; + +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; +import org.eclipse.smarthome.config.discovery.internal.DiscoveryResultImpl; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Tests for {@link InboxPredicates}. + * + * @author Andre Fuechsel - Initial Contribution + */ +public class InboxPredicatesTest { + + private static final String BINDING_ID1 = "bindingId1"; + private static final String BINDING_ID2 = "bindingId2"; + private static final String THING_ID1 = "thingId1"; + private static final String THING_ID2 = "thingId2"; + private static final String THING_TYPE_ID1 = "thingTypeId1"; + private static final String THING_TYPE_ID2 = "thingTypeId2"; + + private static final ThingUID THING_UID11 = new ThingUID(BINDING_ID1, THING_ID1); + private static final ThingUID THING_UID12 = new ThingUID(BINDING_ID1, THING_ID2); + private static final ThingUID THING_UID21 = new ThingUID(BINDING_ID2, THING_ID1); + private static final ThingUID THING_UID22 = new ThingUID(BINDING_ID2, THING_ID2); + + private static final String PROP1 = "prop1"; + private static final String PROP2 = "prop2"; + private static final String PROP_VAL1 = "propVal1"; + private static final String PROP_VAL2 = "propVal2"; + + private final static ThingTypeUID THING_TYPE_UID11 = new ThingTypeUID(BINDING_ID1, THING_TYPE_ID1); + private final static ThingTypeUID THING_TYPE_UID12 = new ThingTypeUID(BINDING_ID1, THING_TYPE_ID2); + private final static ThingTypeUID THING_TYPE_UID21 = new ThingTypeUID(BINDING_ID2, THING_TYPE_ID1); + private final static ThingTypeUID THING_TYPE_UID22 = new ThingTypeUID(BINDING_ID2, THING_TYPE_ID2); + + private final static Map PROPS1 = new ImmutableMap.Builder().put(PROP1, PROP_VAL1) + .put(PROP2, PROP_VAL2).build(); + private final static Map PROPS2 = new ImmutableMap.Builder().put(PROP2, PROP_VAL2) + .build(); + + private final static List results = new ImmutableList.Builder() + .add(new DiscoveryResultImpl(THING_TYPE_UID11, THING_UID11, null, PROPS1, PROP1, "label", + DiscoveryResult.TTL_UNLIMITED)) + .add(new DiscoveryResultImpl(THING_TYPE_UID11, THING_UID12, null, PROPS1, null, "label", + DiscoveryResult.TTL_UNLIMITED)) + .add(new DiscoveryResultImpl(THING_TYPE_UID12, THING_UID12, null, PROPS2, PROP2, "label", + DiscoveryResult.TTL_UNLIMITED)) + .add(new DiscoveryResultImpl(THING_TYPE_UID21, THING_UID22, null, PROPS2, null, "label", + DiscoveryResult.TTL_UNLIMITED)) + .build(); + + @Before + public void setUp() throws Exception { + results.get(3).setFlag(DiscoveryResultFlag.IGNORED); + } + + @Test + public void testForBinding() { + assertThat(results.stream().filter(forBinding(BINDING_ID1)).collect(Collectors.toList()).size(), is(3)); + + assertThat(results.stream().filter(forBinding(BINDING_ID2)).collect(Collectors.toList()).size(), is(1)); + assertThat(results.stream().filter(forBinding(BINDING_ID2)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(3)))); + + assertThat(results.stream().filter(forBinding(BINDING_ID2)).filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()).size(), is(0)); + + assertThat(results.stream().filter(forBinding(BINDING_ID2)).filter(withFlag(DiscoveryResultFlag.IGNORED)) + .collect(Collectors.toList()).size(), is(1)); + assertThat(results.stream().filter(forBinding(BINDING_ID2)).filter(withFlag(DiscoveryResultFlag.IGNORED)) + .collect(Collectors.toList()).get(0), is(equalTo(results.get(3)))); + } + + @Test + public void testForThingTypeUID() { + assertThat(results.stream().filter(forThingTypeUID(THING_TYPE_UID11)).collect(Collectors.toList()).size(), + is(2)); + + assertThat(results.stream().filter(forThingTypeUID(THING_TYPE_UID12)).collect(Collectors.toList()).size(), + is(1)); + assertThat(results.stream().filter(forThingTypeUID(THING_TYPE_UID12)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(2)))); + } + + @Test + public void testForThingUID() { + assertThat(results.stream().filter(forThingUID(THING_UID11)).collect(Collectors.toList()).size(), is(1)); + assertThat(results.stream().filter(forThingUID(THING_UID11)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(0)))); + + assertThat(results.stream().filter(forThingUID(THING_UID12)).collect(Collectors.toList()).size(), is(2)); + assertThat(results.stream().filter(forThingUID(THING_UID12)).filter(forThingTypeUID(THING_TYPE_UID12)) + .collect(Collectors.toList()).size(), is(1)); + assertThat(results.stream().filter(forThingUID(THING_UID12)).filter(forThingTypeUID(THING_TYPE_UID12)) + .collect(Collectors.toList()).get(0), is(equalTo(results.get(2)))); + } + + @Test + public void testWithFlag() { + assertThat(results.stream().filter(withFlag(DiscoveryResultFlag.NEW)).collect(Collectors.toList()).size(), + is(3)); + assertThat(results.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()).size(), + is(1)); + assertThat(results.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(3)))); + } + + @Test + public void testWithProperty() { + assertThat(results.stream().filter(withProperty(PROP1, PROP_VAL1)).collect(Collectors.toList()).size(), is(2)); + assertThat(results.stream().filter(withProperty(PROP2, PROP_VAL2)).collect(Collectors.toList()).size(), is(4)); + assertThat(results.stream().filter(withProperty(PROP1, PROP_VAL2)).collect(Collectors.toList()).size(), is(0)); + assertThat(results.stream().filter(withProperty(PROP2, PROP_VAL1)).collect(Collectors.toList()).size(), is(0)); + assertThat(results.stream().filter(withProperty(null, PROP_VAL1)).collect(Collectors.toList()).size(), is(0)); + } + + @Test + public void testWithRepresentationProperty() { + assertThat(results.stream().filter(withRepresentationProperty(PROP1)).collect(Collectors.toList()).size(), + is(1)); + assertThat(results.stream().filter(withRepresentationProperty(PROP1)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(0)))); + assertThat(results.stream().filter(withRepresentationProperty(PROP2)).collect(Collectors.toList()).size(), + is(1)); + assertThat(results.stream().filter(withRepresentationProperty(PROP2)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(2)))); + } + + @Test + public void testWithRepresentationPropertyValue() { + assertThat( + results.stream().filter(withRepresentationPropertyValue(PROP_VAL1)).collect(Collectors.toList()).size(), + is(1)); + assertThat( + results.stream().filter(withRepresentationPropertyValue(PROP_VAL1)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(0)))); + assertThat( + results.stream().filter(withRepresentationPropertyValue(PROP_VAL2)).collect(Collectors.toList()).size(), + is(1)); + assertThat( + results.stream().filter(withRepresentationPropertyValue(PROP_VAL2)).collect(Collectors.toList()).get(0), + is(equalTo(results.get(2)))); + } + +} diff --git a/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessorTest.java b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessorTest.java new file mode 100644 index 00000000000..d62b2b0df0f --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery.test/src/test/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessorTest.java @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.config.discovery.internal; + +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.withFlag; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; +import org.eclipse.smarthome.core.thing.ManagedThingProvider; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingRegistry; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingStatusInfo; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; +import org.eclipse.smarthome.core.thing.events.ThingStatusInfoChangedEvent; +import org.eclipse.smarthome.core.thing.type.ThingType; +import org.eclipse.smarthome.core.thing.type.ThingTypeRegistry; +import org.eclipse.smarthome.test.storage.VolatileStorageService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import com.google.common.collect.ImmutableMap; + +/** + * @author Andre Fuechsel - Initial contribution + */ +public class AutomaticInboxProcessorTest { + + private static final String DEVICE_ID = "deviceId"; + private static final String DEVICE_ID_KEY = "deviceIdKey"; + private static final String CONFIG_KEY = "configKey"; + private static final String CONFIG_VALUE = "configValue"; + + private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("test", "test"); + private static final ThingTypeUID THING_TYPE_UID2 = new ThingTypeUID("test2", "test2"); + private static final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, "test"); + private static final ThingUID THING_UID2 = new ThingUID(THING_TYPE_UID, "test2"); + private static final ThingType THING_TYPE = new ThingType(THING_TYPE_UID, null, "label", null, true, DEVICE_ID_KEY, + null, null, null, null); + private static final ThingType THING_TYPE2 = new ThingType(THING_TYPE_UID2, null, "label", null, true, CONFIG_KEY, + null, null, null, null); + private final static Map THING_PROPERTIES = new ImmutableMap.Builder() + .put(DEVICE_ID_KEY, DEVICE_ID).build(); + private final static Configuration CONFIG = new Configuration( + new ImmutableMap.Builder().put(CONFIG_KEY, CONFIG_VALUE).build()); + + private AutomaticInboxProcessor inboxAutoIgnore; + private PersistentInbox inbox; + + @Mock + private ThingRegistry thingRegistry; + + @Mock + private ThingTypeRegistry thingTypeRegistry; + + @Mock + private Thing thing; + + @Mock + private Thing thing2; + + @Mock + private ThingStatusInfoChangedEvent thingStatusInfoChangedEvent; + + @Mock + private ConfigDescriptionRegistry configDescriptionRegistry; + + @Mock + private ThingHandlerFactory thingHandlerFactory; + + @Mock + private ManagedThingProvider thingProvider; + + @Before + public void setUp() throws Exception { + initMocks(this); + + when(thing.getConfiguration()).thenReturn(CONFIG); + when(thing.getThingTypeUID()).thenReturn(THING_TYPE_UID); + when(thing.getProperties()).thenReturn(THING_PROPERTIES); + when(thing.getStatus()).thenReturn(ThingStatus.ONLINE); + when(thing.getUID()).thenReturn(THING_UID); + + when(thing2.getConfiguration()).thenReturn(CONFIG); + when(thing2.getThingTypeUID()).thenReturn(THING_TYPE_UID2); + when(thing2.getProperties()).thenReturn(THING_PROPERTIES); + when(thing2.getStatus()).thenReturn(ThingStatus.ONLINE); + when(thing2.getUID()).thenReturn(THING_UID2); + + when(thingRegistry.stream()).thenReturn(Stream.empty()); + + when(thingTypeRegistry.getThingType(THING_TYPE_UID)).thenReturn(THING_TYPE); + when(thingTypeRegistry.getThingType(THING_TYPE_UID2)).thenReturn(THING_TYPE2); + + when(thingHandlerFactory.supportsThingType(eq(THING_TYPE_UID))).thenReturn(true); + when(thingHandlerFactory.createThing(eq(THING_TYPE_UID), any(Configuration.class), eq(THING_UID), + any(ThingUID.class))) + .then(invocation -> ThingBuilder.create(THING_TYPE_UID, "test") + .withConfiguration((Configuration) invocation.getArguments()[1]).build()); + + inbox = new PersistentInbox(); + inbox.setThingRegistry(thingRegistry); + inbox.setStorageService(new VolatileStorageService()); + inbox.setManagedThingProvider(thingProvider); + inbox.setConfigDescriptionRegistry(configDescriptionRegistry); + inbox.setThingTypeRegistry(thingTypeRegistry); + inbox.addThingHandlerFactory(thingHandlerFactory); + + inboxAutoIgnore = new AutomaticInboxProcessor(); + inboxAutoIgnore.setThingRegistry(thingRegistry); + inboxAutoIgnore.setThingTypeRegistry(thingTypeRegistry); + inboxAutoIgnore.setInbox(inbox); + } + + @Test + public void testThingWentOnline() { + inbox.add(DiscoveryResultBuilder.create(THING_UID).withProperty(DEVICE_ID_KEY, DEVICE_ID) + .withRepresentationProperty(DEVICE_ID_KEY).build()); + + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID))); + + when(thingRegistry.get(THING_UID)).thenReturn(thing); + when(thingStatusInfoChangedEvent.getStatusInfo()) + .thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null)); + when(thingStatusInfoChangedEvent.getThingUID()).thenReturn(THING_UID); + inboxAutoIgnore.receive(thingStatusInfoChangedEvent); + + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)).collect(Collectors.toList()); + assertThat(results.size(), is(0)); + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID))); + } + + @Test + public void testNoDiscoveryResultIfNoRepresentationPropertySet() { + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(0)); + } + + @Test + public void testThingWhenNoRepresentationPropertySet() { + inbox.add(DiscoveryResultBuilder.create(THING_UID).withProperty(DEVICE_ID_KEY, DEVICE_ID).build()); + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID))); + + when(thing.getProperties()).thenReturn(Collections.emptyMap()); + when(thingStatusInfoChangedEvent.getStatusInfo()) + .thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null)); + when(thingStatusInfoChangedEvent.getThingUID()).thenReturn(THING_UID); + inboxAutoIgnore.receive(thingStatusInfoChangedEvent); + + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + assertThat(results.size(), is(0)); + } + + @Test + public void testInboxHasBeenChanged() { + inbox.stream().map(DiscoveryResult::getThingUID).forEach(t -> inbox.remove(t)); + assertThat(inbox.getAll().size(), is(0)); + + when(thingRegistry.get(THING_UID)).thenReturn(thing); + when(thingRegistry.stream()).thenReturn(Stream.of(thing)); + + inbox.add(DiscoveryResultBuilder.create(THING_UID2).withProperty(DEVICE_ID_KEY, DEVICE_ID) + .withRepresentationProperty(DEVICE_ID_KEY).build()); + + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(0)); + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID2))); + } + + @Test + public void testThingIsBeingRemoved() { + inbox.add(DiscoveryResultBuilder.create(THING_UID).withProperty(DEVICE_ID_KEY, DEVICE_ID) + .withRepresentationProperty(DEVICE_ID_KEY).build()); + + inbox.setFlag(THING_UID, DiscoveryResultFlag.IGNORED); + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)) + .collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID))); + + when(thingRegistry.get(THING_UID)).thenReturn(thing); + when(thingStatusInfoChangedEvent.getStatusInfo()) + .thenReturn(new ThingStatusInfo(ThingStatus.REMOVING, ThingStatusDetail.NONE, null)); + when(thingStatusInfoChangedEvent.getThingUID()).thenReturn(THING_UID); + inboxAutoIgnore.receive(thingStatusInfoChangedEvent); + + results = inbox.getAll(); + assertThat(results.size(), is(0)); + } + + @Test + public void testThingWithConfigWentOnline() { + inbox.add(DiscoveryResultBuilder.create(THING_UID2).withProperty(CONFIG_KEY, CONFIG_VALUE) + .withRepresentationProperty(CONFIG_KEY).build()); + + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID2))); + + when(thingRegistry.get(THING_UID2)).thenReturn(thing2); + when(thingStatusInfoChangedEvent.getStatusInfo()) + .thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null)); + when(thingStatusInfoChangedEvent.getThingUID()).thenReturn(THING_UID2); + inboxAutoIgnore.receive(thingStatusInfoChangedEvent); + + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)).collect(Collectors.toList()); + assertThat(results.size(), is(0)); + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID2))); + } + + @Test + public void testInboxWithConfigHasBeenChanged() { + inbox.stream().map(DiscoveryResult::getThingUID).forEach(t -> inbox.remove(t)); + assertThat(inbox.getAll().size(), is(0)); + + when(thingRegistry.get(THING_UID2)).thenReturn(thing2); + when(thingRegistry.stream()).thenReturn(Stream.of(thing2)); + + inbox.add(DiscoveryResultBuilder.create(THING_UID).withProperty(CONFIG_KEY, CONFIG_VALUE) + .withRepresentationProperty(CONFIG_KEY).build()); + + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.NEW)) + .collect(Collectors.toList()); + assertThat(results.size(), is(0)); + results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID))); + } + + @Test + public void testThingWithConfigIsBeingRemoved() { + inbox.add(DiscoveryResultBuilder.create(THING_UID2).withProperty(CONFIG_KEY, CONFIG_VALUE) + .withRepresentationProperty(CONFIG_KEY).build()); + + inbox.setFlag(THING_UID2, DiscoveryResultFlag.IGNORED); + List results = inbox.stream().filter(withFlag(DiscoveryResultFlag.IGNORED)) + .collect(Collectors.toList()); + assertThat(results.size(), is(1)); + assertThat(results.get(0).getThingUID(), is(equalTo(THING_UID2))); + + when(thingRegistry.get(THING_UID2)).thenReturn(thing2); + when(thingStatusInfoChangedEvent.getStatusInfo()) + .thenReturn(new ThingStatusInfo(ThingStatus.REMOVING, ThingStatusDetail.NONE, null)); + when(thingStatusInfoChangedEvent.getThingUID()).thenReturn(THING_UID2); + inboxAutoIgnore.receive(thingStatusInfoChangedEvent); + + results = inbox.getAll(); + assertThat(results.size(), is(0)); + } + +} diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/ESH-INF/config/config.xml b/bundles/config/org.eclipse.smarthome.config.discovery/ESH-INF/config/config.xml new file mode 100644 index 00000000000..1a8834cc691 --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery/ESH-INF/config/config.xml @@ -0,0 +1,20 @@ + + + + + + + If enabled, an inbox result matching an existing thing is automatically ignored.

+

If set to false, the inbox may contain results although identical things already exist.

+ ]]> +
+ true +
+
+ +
\ No newline at end of file diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/META-INF/MANIFEST.MF b/bundles/config/org.eclipse.smarthome.config.discovery/META-INF/MANIFEST.MF index 5570fc87133..44e6ac46eb9 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/META-INF/MANIFEST.MF +++ b/bundles/config/org.eclipse.smarthome.config.discovery/META-INF/MANIFEST.MF @@ -7,6 +7,7 @@ Bundle-Vendor: Eclipse.org/SmartHome Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.google.common.base, com.google.common.collect, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.config.discovery.dto, @@ -18,6 +19,7 @@ Import-Package: com.google.common.base, org.eclipse.smarthome.core.storage, org.eclipse.smarthome.core.thing, org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.events, org.eclipse.smarthome.core.thing.type, org.eclipse.smarthome.io.console, org.eclipse.smarthome.io.console.extensions, @@ -29,6 +31,7 @@ Import-Package: com.google.common.base, org.osgi.framework, org.osgi.service.cm, org.osgi.service.component, + org.osgi.service.component.annotations;resolution:=optional, org.slf4j Service-Component: OSGI-INF/*.xml Export-Package: org.eclipse.smarthome.config.discovery, diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/OSGI-INF/.gitignore b/bundles/config/org.eclipse.smarthome.config.discovery/OSGI-INF/.gitignore new file mode 100644 index 00000000000..3dc79f73c09 --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/org.eclipse.smarthome.config.discovery.internal.AutomaticInboxProcessor.xml diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/build.properties b/bundles/config/org.eclipse.smarthome.config.discovery/build.properties index 9beae6ace39..050539ec5e8 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/build.properties +++ b/bundles/config/org.eclipse.smarthome.config.discovery/build.properties @@ -3,4 +3,5 @@ output.. = target/classes/ bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - about.html + about.html,\ + ESH-INF/ diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/AbstractDiscoveryService.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/AbstractDiscoveryService.java index 68a7308fc57..48a159488fc 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/AbstractDiscoveryService.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/AbstractDiscoveryService.java @@ -43,7 +43,8 @@ public abstract class AbstractDiscoveryService implements DiscoveryService { private final Logger logger = LoggerFactory.getLogger(AbstractDiscoveryService.class); - static protected final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(DISCOVERY_THREADPOOL_NAME); + static protected final ScheduledExecutorService scheduler = ThreadPoolManager + .getScheduledPool(DISCOVERY_THREADPOOL_NAME); private Set discoveryListeners = new CopyOnWriteArraySet<>(); protected ScanListener scanListener = null; @@ -252,8 +253,8 @@ protected void thingDiscovered(DiscoveryResult discoveryResult) { try { discoveryListener.thingDiscovered(this, discoveryResult); } catch (Exception e) { - logger.error("An error occurred while calling the discovery listener " - + discoveryListener.getClass().getName() + ".", e); + logger.error("An error occurred while calling the discovery listener {}.", + discoveryListener.getClass().getName(), e); } } synchronized (cachedResults) { @@ -272,8 +273,8 @@ protected void thingRemoved(ThingUID thingUID) { try { discoveryListener.thingRemoved(this, thingUID); } catch (Exception e) { - logger.error("An error occurred while calling the discovery listener " - + discoveryListener.getClass().getName() + ".", e); + logger.error("An error occurred while calling the discovery listener {}.", + discoveryListener.getClass().getName(), e); } } synchronized (cachedResults) { @@ -316,8 +317,8 @@ protected void removeOlderResults(long timestamp, Collection thing try { removedThings = discoveryListener.removeOlderResults(this, timestamp, thingTypeUIDs); } catch (Exception e) { - logger.error("An error occurred while calling the discovery listener " - + discoveryListener.getClass().getName() + ".", e); + logger.error("An error occurred while calling the discovery listener {}.", + discoveryListener.getClass().getName(), e); } } if (removedThings != null) { diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/DiscoveryResult.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/DiscoveryResult.java index f7921fda85f..b200d25594a 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/DiscoveryResult.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/DiscoveryResult.java @@ -80,7 +80,7 @@ public interface DiscoveryResult { * discovered. Its actual value can be retrieved from the {@link DiscoveryResult#getProperties()} map. Such unique * identifiers are typically the ipAddress, the macAddress or the * serialNumber of the discovered thing. - * + * * @return the representation property of this result object (could be null) */ public String getRepresentationProperty(); @@ -112,14 +112,14 @@ public interface DiscoveryResult { /** * Get the timestamp of this {@link DiscoveryResult}. - * + * * @return timestamp as long */ public long getTimestamp(); /** * Get the time to live in seconds for this entry. - * + * * @return time to live in seconds */ public long getTimeToLive(); diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/dto/DiscoveryResultDTOMapper.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/dto/DiscoveryResultDTOMapper.java index 3d38928d420..e65d2e7bea5 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/dto/DiscoveryResultDTOMapper.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/dto/DiscoveryResultDTOMapper.java @@ -8,11 +8,15 @@ package org.eclipse.smarthome.config.discovery.dto; import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; /** * The {@link DiscoveryResultDTOMapper} is an utility class to map discovery results into discovery result transfer * objects. + * + * @author Stefan Bussweiler - Initial contribution */ public class DiscoveryResultDTOMapper { @@ -31,4 +35,22 @@ public static DiscoveryResultDTO map(DiscoveryResult discoveryResult) { discoveryResult.getLabel(), discoveryResult.getFlag(), discoveryResult.getProperties(), discoveryResult.getRepresentationProperty()); } + + /** + * Maps discovery result data transfer object into discovery result. + * + * @param discoveryResultDTO the discovery result data transfer object + * @return the discovery result + */ + public static DiscoveryResult map(DiscoveryResultDTO discoveryResultDTO) { + ThingUID thingUID = new ThingUID(discoveryResultDTO.thingUID); + ThingTypeUID thingTypeUID = discoveryResultDTO.thingTypeUID != null + ? new ThingTypeUID(discoveryResultDTO.thingTypeUID) : null; + ThingUID bridgeUID = discoveryResultDTO.bridgeUID != null ? new ThingUID(discoveryResultDTO.bridgeUID) : null; + + return DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withBridge(bridgeUID) + .withLabel(discoveryResultDTO.label) + .withRepresentationProperty(discoveryResultDTO.representationProperty) + .withProperties(discoveryResultDTO.properties).build(); + } } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/Inbox.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/Inbox.java index e173a329cbf..1b6a606acfe 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/Inbox.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/Inbox.java @@ -8,6 +8,7 @@ package org.eclipse.smarthome.config.discovery.inbox; import java.util.List; +import java.util.stream.Stream; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; @@ -75,7 +76,10 @@ public interface Inbox { * * @return all discovery results in this inbox which fit to the specified filter criteria * (not null, could be empty) + * + * @deprecated use {@link InboxPredicates} to filter on streams of {@link DiscoveryResult}s */ + @Deprecated List get(InboxFilterCriteria criteria); /** @@ -85,6 +89,13 @@ public interface Inbox { */ List getAll(); + /** + * Returns a stream of all {@link DiscoveryResult}s in this {@link Inbox}. + * + * @return stream of all discovery results in this inbox + */ + Stream stream(); + /** * Sets the flag for a given thingUID result.
* The flag signals e.g. if the result is {@link DiscoveryResultFlag#NEW} or has been marked as @@ -121,14 +132,14 @@ public interface Inbox { * @param listener the listener to be removed (could be null) */ void removeInboxListener(InboxListener listener); - + /** - * Creates new {@link Thing} and adds it to the {@link ThingRegistry}. - * - * @param thingUID the UID of the Thing - * @param label the label of the Thing + * Creates new {@link Thing} and adds it to the {@link ThingRegistry}. + * + * @param thingUID the UID of the Thing + * @param label the label of the Thing * @return the approved Thing */ - Thing approve(ThingUID thingUID, String label); + Thing approve(ThingUID thingUID, String label); } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxFilterCriteria.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxFilterCriteria.java index 3a22e6f781e..6d8cf34b338 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxFilterCriteria.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxFilterCriteria.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.config.discovery.inbox; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; @@ -20,7 +21,10 @@ * @author Michael Grammling - Initial Contribution * * @see Inbox + * + * @deprecated use {@link InboxPredicates} to filter on streams of {@link DiscoveryResult}s */ +@Deprecated public final class InboxFilterCriteria { private final String bindingId; diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicates.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicates.java new file mode 100644 index 00000000000..0867f9e0b0d --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/inbox/InboxPredicates.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.config.discovery.inbox; + +import java.util.function.Predicate; + +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; + +/** + * Implements static factory methods for {@link Predicate}s to filter in streams of {@link DiscoveryResult}s. + * + * @author Andre Fuechsel - Initial Contribution + */ +public class InboxPredicates { + + public static Predicate forBinding(String bindingId) { + return r -> bindingId != null && bindingId.equals(r.getBindingId()); + } + + public static Predicate forThingTypeUID(ThingTypeUID uid) { + return r -> uid != null && uid.equals(r.getThingTypeUID()); + } + + public static Predicate forThingUID(ThingUID thingUID) { + return r -> thingUID != null && thingUID.equals(r.getThingUID()); + } + + public static Predicate withFlag(DiscoveryResultFlag flag) { + return r -> flag == r.getFlag(); + } + + public static Predicate withProperty(String propertyName, String propertyValue) { + return r -> r.getProperties().containsKey(propertyName) + && r.getProperties().get(propertyName).equals(propertyValue); + } + + public static Predicate withRepresentationProperty(String propertyName) { + return r -> propertyName != null && propertyName.equals(r.getRepresentationProperty()); + } + + public static Predicate withRepresentationPropertyValue(String propertyValue) { + return r -> propertyValue != null && propertyValue.equals(r.getProperties().get(r.getRepresentationProperty())); + } +} diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessor.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessor.java new file mode 100644 index 00000000000..1eb24225abd --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/AutomaticInboxProcessor.java @@ -0,0 +1,204 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.config.discovery.internal; + +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.*; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; +import org.eclipse.smarthome.config.discovery.inbox.Inbox; +import org.eclipse.smarthome.config.discovery.inbox.InboxListener; +import org.eclipse.smarthome.core.events.AbstractTypedEventSubscriber; +import org.eclipse.smarthome.core.events.EventSubscriber; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingRegistry; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.events.ThingStatusInfoChangedEvent; +import org.eclipse.smarthome.core.thing.type.ThingType; +import org.eclipse.smarthome.core.thing.type.ThingTypeRegistry; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements a service to automatically ignore {@link Inbox} entries of newly created things. + *

+ * The {@link AutomaticInboxProcessor} service implements a {@link EventSubscriber}, that is triggered + * for each thing when coming ONLINE. {@link Inbox} entries with the same representation value like the + * newly created thing will be automatically set to {@link DiscoveryResultFlag#IGNORED}. + *

+ *

+ * If a thing is being removed, possibly existing {@link Inbox} entries with the same representation value + * are removed from the {@link Inbox} so they could be discovered again afterwards. + *

+ *

+ * This service can be enabled or disabled by setting the {@code autoIgnore} property to either + * {@code true} or {@code false} via ConfigAdmin. + *

+ * + * @author Andre Fuechsel - Initial Contribution + */ +@Component(immediate = true, service = EventSubscriber.class, property = { + "service.config.description.uri=system:inbox", "service.config.label=Inbox", "service.config.category=system", + "service.pid=org.eclipse.smarthome.inbox" }) +public class AutomaticInboxProcessor extends AbstractTypedEventSubscriber + implements InboxListener { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private ThingRegistry thingRegistry; + private ThingTypeRegistry thingTypeRegistry; + private Inbox inbox; + private boolean autoIgnore = true; + + public AutomaticInboxProcessor() { + super(ThingStatusInfoChangedEvent.TYPE); + } + + @Override + public void receiveTypedEvent(ThingStatusInfoChangedEvent event) { + if (autoIgnore) { + Thing thing = thingRegistry.get(event.getThingUID()); + ThingStatus thingStatus = event.getStatusInfo().getStatus(); + autoIgnore(thing, thingStatus); + } + } + + @Override + public void thingAdded(Inbox inbox, DiscoveryResult result) { + if (autoIgnore) { + String value = getRepresentationValue(result); + Thing thing = thingRegistry.stream() + .filter(t -> Objects.equals(value, getRepresentationPropertyValueForThing(t))) + .findFirst() + .orElse(null); + if (thing != null) { + logger.debug("Auto-ignoring the inbox entry for the representation value {}", value); + inbox.setFlag(result.getThingUID(), DiscoveryResultFlag.IGNORED); + } + } + } + + private String getRepresentationValue(DiscoveryResult result) { + return result.getRepresentationProperty() != null + ? Objects.toString(result.getProperties().get(result.getRepresentationProperty()), null) + : null; + } + + @Override + public void thingUpdated(Inbox inbox, DiscoveryResult result) { + } + + @Override + public void thingRemoved(Inbox inbox, DiscoveryResult result) { + } + + private void autoIgnore(Thing thing, ThingStatus thingStatus) { + if (ThingStatus.ONLINE.equals(thingStatus)) { + checkAndIgnoreInInbox(thing); + } else if (ThingStatus.REMOVING.equals(thingStatus)) { + removePossiblyIgnoredResultInInbox(thing); + } + } + + private void checkAndIgnoreInInbox(Thing thing) { + if (thing != null) { + String representationValue = getRepresentationPropertyValueForThing(thing); + if (representationValue != null) { + ignoreInInbox(representationValue); + } + } + } + + private void ignoreInInbox(String representationValue) { + List results = inbox.stream().filter(withRepresentationPropertyValue(representationValue)) + .collect(Collectors.toList()); + if (results.size() == 1) { + logger.debug("Auto-ignoring the inbox entry for the representation value {}", representationValue); + inbox.setFlag(results.get(0).getThingUID(), DiscoveryResultFlag.IGNORED); + } + } + + private void removePossiblyIgnoredResultInInbox(Thing thing) { + if (thing != null) { + String representationValue = getRepresentationPropertyValueForThing(thing); + if (representationValue != null) { + removeFromInbox(representationValue); + } + } + } + + private String getRepresentationPropertyValueForThing(Thing thing) { + ThingType thingType = thingTypeRegistry.getThingType(thing.getThingTypeUID()); + String representationProperty = thingType.getRepresentationProperty(); + Map properties = thing.getProperties(); + if (properties.containsKey(representationProperty)) { + return properties.get(representationProperty); + } + Configuration configuration = thing.getConfiguration(); + if (configuration.containsKey(representationProperty)) { + return String.valueOf(configuration.get(representationProperty)); + } + return null; + } + + private void removeFromInbox(String representationValue) { + List results = inbox.stream().filter(withRepresentationPropertyValue(representationValue)) + .filter(withFlag(DiscoveryResultFlag.IGNORED)).collect(Collectors.toList()); + if (results.size() == 1) { + logger.debug("Removing the ignored result from the inbox for the representation value {}", + representationValue); + inbox.remove(results.get(0).getThingUID()); + } + } + + @Activate + protected void activate(Map properties) { + if (properties != null) { + Object value = properties.get("autoIgnore"); + autoIgnore = value == null || !value.toString().equals("false"); + } + } + + @Reference + protected void setThingRegistry(ThingRegistry thingRegistry) { + this.thingRegistry = thingRegistry; + } + + protected void unsetThingRegistry(ThingRegistry thingRegistry) { + this.thingRegistry = null; + } + + @Reference + protected void setThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) { + this.thingTypeRegistry = thingTypeRegistry; + } + + protected void unsetThingTypeRegistry(ThingTypeRegistry thingTypeRegistry) { + this.thingTypeRegistry = null; + } + + @Reference + protected void setInbox(Inbox inbox) { + this.inbox = inbox; + inbox.addInboxListener(this); + } + + protected void unsetInbox(Inbox inbox) { + inbox.removeInboxListener(this); + this.inbox = null; + } +} diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryResultImpl.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryResultImpl.java index 705e3167f4d..5b61c0f9b6d 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryResultImpl.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryResultImpl.java @@ -17,6 +17,10 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class DiscoveryResultImpl implements DiscoveryResult { private ThingUID bridgeUID; @@ -79,7 +83,7 @@ public DiscoveryResultImpl(ThingUID thingUID, ThingUID bridgeUID, Map properties, String representationProperty, String label, long timeToLive) - throws IllegalArgumentException { + throws IllegalArgumentException { if (thingUID == null) { throw new IllegalArgumentException("The thing UID must not be null!"); } @@ -196,25 +200,31 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } DiscoveryResultImpl other = (DiscoveryResultImpl) obj; if (thingUID == null) { - if (other.thingUID != null) + if (other.thingUID != null) { return false; - } else if (!thingUID.equals(other.thingUID)) + } + } else if (!thingUID.equals(other.thingUID)) { return false; + } return true; } @Override public String toString() { - return "DiscoveryResult [thingUID=" + thingUID + ", properties=" + properties + ", flag=" + flag + ", label=" - + label + ", bridgeUID=" + bridgeUID + ", ttl=" + timeToLive + ", timestamp=" + timestamp + "]"; + return "DiscoveryResult [thingUID=" + thingUID + ", properties=" + properties + ", representationProperty=" + + representationProperty + ", flag=" + flag + ", label=" + label + ", bridgeUID=" + bridgeUID + ", ttl=" + + timeToLive + ", timestamp=" + timestamp + "]"; } @Override diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryServiceRegistryImpl.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryServiceRegistryImpl.java index d0a73ed68c1..bb258e7dfb0 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryServiceRegistryImpl.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/DiscoveryServiceRegistryImpl.java @@ -7,6 +7,8 @@ */ package org.eclipse.smarthome.config.discovery.internal; +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.*; + import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; @@ -19,6 +21,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Collectors; import org.eclipse.smarthome.config.discovery.DiscoveryListener; import org.eclipse.smarthome.config.discovery.DiscoveryResult; @@ -29,7 +32,6 @@ import org.eclipse.smarthome.config.discovery.ExtendedDiscoveryService; import org.eclipse.smarthome.config.discovery.ScanListener; import org.eclipse.smarthome.config.discovery.inbox.Inbox; -import org.eclipse.smarthome.config.discovery.inbox.InboxFilterCriteria; import org.eclipse.smarthome.core.common.SafeMethodCaller; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingRegistry; @@ -147,7 +149,7 @@ public DiscoveryResult getExistingDiscoveryResult(ThingUID thingUID) { return null; } List ret = new ArrayList<>(); - ret = inboxReference.get(new InboxFilterCriteria(thingUID, DiscoveryResultFlag.NEW)); + ret = inboxReference.stream().filter(withFlag((DiscoveryResultFlag.NEW))).collect(Collectors.toList()); if (ret.size() > 0) { return ret.get(0); } else { @@ -276,8 +278,8 @@ public Void run() { } }); } catch (Exception ex) { - logger.error("Cannot notify the DiscoveryListener " + listener.getClass().getName() - + " on Thing discovered event!", ex); + logger.error("Cannot notify the DiscoveryListener {} on Thing discovered event!", + listener.getClass().getName(), ex); } } } @@ -302,8 +304,8 @@ public Void run() { } }); } catch (Exception ex) { - logger.error("Cannot notify the DiscoveryListener '" + listener.getClass().getName() - + "' on Thing removed event!", ex); + logger.error("Cannot notify the DiscoveryListener '{}' on Thing removed event!", + listener.getClass().getName(), ex); } } } @@ -325,8 +327,8 @@ public Collection run() { removedResults.addAll(olderResults); } } catch (Exception ex) { - logger.error("Cannot notify the DiscoveryListener '" + listener.getClass().getName() - + "' on all things removed event!", ex); + logger.error("Cannot notify the DiscoveryListener '{}' on all things removed event!", + listener.getClass().getName(), ex); } } @@ -347,8 +349,8 @@ private boolean abortScans(Set discoveryServices) { logger.debug("Scan for thing types '{}' aborted on '{}'.", supportedThingTypes, discoveryService.getClass().getName()); } catch (Exception ex) { - logger.error("Cannot abort scan for thing types '" + supportedThingTypes + "' on '" - + discoveryService.getClass().getName() + "'!", ex); + logger.error("Cannot abort scan for thing types '{}' on '{}'!", supportedThingTypes, + discoveryService.getClass().getName(), ex); allServicesAborted = false; } } @@ -392,8 +394,8 @@ private boolean startScan(DiscoveryService discoveryService, ScanListener listen discoveryService.startScan(listener); return true; } catch (Exception ex) { - logger.error("Cannot trigger scan for thing types '" + supportedThingTypes + "' on '" - + discoveryService.getClass().getSimpleName() + "'!", ex); + logger.error("Cannot trigger scan for thing types '{}' on '{}'!", supportedThingTypes, + discoveryService.getClass().getSimpleName(), ex); return false; } } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/PersistentInbox.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/PersistentInbox.java index e963a5d14f8..4a8ff010f83 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/PersistentInbox.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/PersistentInbox.java @@ -7,6 +7,8 @@ */ package org.eclipse.smarthome.config.discovery.internal; +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.forThingUID; + import java.net.URI; import java.util.ArrayList; import java.util.Collection; @@ -23,6 +25,8 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; @@ -115,25 +119,15 @@ private boolean isResultExpired(DiscoveryResult result, long now) { private final Logger logger = LoggerFactory.getLogger(PersistentInbox.class); private Set listeners = new CopyOnWriteArraySet<>(); - private DiscoveryServiceRegistry discoveryServiceRegistry; - private ThingRegistry thingRegistry; - private ManagedThingProvider managedThingProvider; - private ThingTypeRegistry thingTypeRegistry; - private ConfigDescriptionRegistry configDescRegistry; - private Storage discoveryResultStorage; - private Map> resultDiscovererMap = new ConcurrentHashMap<>(); - private ScheduledFuture timeToLiveChecker; - private EventPublisher eventPublisher; - private List thingHandlerFactories = new CopyOnWriteArrayList<>(); @Override @@ -141,7 +135,7 @@ public Thing approve(ThingUID thingUID, String label) { if (thingUID == null) { throw new IllegalArgumentException("Thing UID must not be null"); } - List results = get(new InboxFilterCriteria(thingUID, null)); + List results = stream().filter(forThingUID(thingUID)).collect(Collectors.toList()); if (results.isEmpty()) { throw new IllegalArgumentException("No Thing with UID " + thingUID.getAsString() + " in inbox"); } @@ -280,7 +274,12 @@ public List get(InboxFilterCriteria criteria) throws IllegalSta @Override public List getAll() { - return get((InboxFilterCriteria) null); + return stream().collect(Collectors.toList()); + } + + @Override + public Stream stream() { + return this.discoveryResultStorage.getValues().stream(); } @Override @@ -463,7 +462,7 @@ private void postEvent(DiscoveryResult result, EventType eventType) { break; } } catch (Exception ex) { - logger.error("Could not post event of type '" + eventType.name() + "'.", ex); + logger.error("Could not post event of type '{}'.", eventType.name(), ex); } } } diff --git a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/console/InboxConsoleCommandExtension.java b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/console/InboxConsoleCommandExtension.java index 8cc937f3bf2..abb9df0b831 100644 --- a/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/console/InboxConsoleCommandExtension.java +++ b/bundles/config/org.eclipse.smarthome.config.discovery/src/main/java/org/eclipse/smarthome/config/discovery/internal/console/InboxConsoleCommandExtension.java @@ -7,15 +7,17 @@ */ package org.eclipse.smarthome.config.discovery.internal.console; +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.*; + import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag; import org.eclipse.smarthome.config.discovery.inbox.Inbox; -import org.eclipse.smarthome.config.discovery.inbox.InboxFilterCriteria; import org.eclipse.smarthome.config.discovery.internal.PersistentInbox; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; @@ -24,7 +26,7 @@ /** * This class provides console commands around the inbox functionality - * + * * @author Kai Kreuzer - Initial contribution and API */ public class InboxConsoleCommandExtension extends AbstractConsoleCommandExtension { @@ -50,7 +52,8 @@ public void execute(String[] args, Console console) { String label = args[2]; try { ThingUID thingUID = new ThingUID(args[1]); - List results = inbox.get(new InboxFilterCriteria(thingUID, null)); + List results = inbox.stream().filter(forThingUID(thingUID)) + .collect(Collectors.toList()); if (results.isEmpty()) { console.println("No matching inbox entry could be found."); return; @@ -77,16 +80,18 @@ public void execute(String[] args, Console console) { } break; case SUBCMD_LIST_IGNORED: - printInboxEntries(console, inbox.get(new InboxFilterCriteria(DiscoveryResultFlag.IGNORED))); + printInboxEntries(console, inbox.stream().filter(withFlag((DiscoveryResultFlag.IGNORED))) + .collect(Collectors.toList())); break; case SUBCMD_CLEAR: - clearInboxEntries(console, inbox.get(new InboxFilterCriteria(DiscoveryResultFlag.NEW))); + clearInboxEntries(console, inbox.getAll()); break; default: break; } } else { - printInboxEntries(console, inbox.get(new InboxFilterCriteria(DiscoveryResultFlag.NEW))); + printInboxEntries(console, + inbox.stream().filter(withFlag((DiscoveryResultFlag.NEW))).collect(Collectors.toList())); } } @@ -103,12 +108,14 @@ private void printInboxEntries(Console console, List discoveryR DiscoveryResultFlag flag = discoveryResult.getFlag(); ThingUID bridgeId = discoveryResult.getBridgeUID(); Map properties = discoveryResult.getProperties(); + String representationProperty = discoveryResult.getRepresentationProperty(); String timestamp = new Date(discoveryResult.getTimestamp()).toString(); String timeToLive = discoveryResult.getTimeToLive() == DiscoveryResult.TTL_UNLIMITED ? "UNLIMITED" : "" + discoveryResult.getTimeToLive(); - console.println( - String.format("%s [%s]: %s [thingId=%s, bridgeId=%s, properties=%s, timestamp=%s, timeToLive=%s]", - flag.name(), thingTypeUID, label, thingUID, bridgeId, properties, timestamp, timeToLive)); + console.println(String.format( + "%s [%s]: %s [thingId=%s, bridgeId=%s, properties=%s, representationProperty=%s, timestamp=%s, timeToLive=%s]", + flag.name(), thingTypeUID, label, thingUID, bridgeId, properties, representationProperty, timestamp, + timeToLive)); } } diff --git a/bundles/config/org.eclipse.smarthome.config.dispatch.test/META-INF/MANIFEST.MF b/bundles/config/org.eclipse.smarthome.config.dispatch.test/META-INF/MANIFEST.MF index 2454f4a1391..e8a284134ec 100644 --- a/bundles/config/org.eclipse.smarthome.config.dispatch.test/META-INF/MANIFEST.MF +++ b/bundles/config/org.eclipse.smarthome.config.dispatch.test/META-INF/MANIFEST.MF @@ -16,4 +16,4 @@ Import-Package: groovy.lang, org.junit, org.osgi.framework, org.osgi.service.component -Service-Component: OSGI-INF/configdispatchertest.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/config/org.eclipse.smarthome.config.dispatch.test/about.html b/bundles/config/org.eclipse.smarthome.config.dispatch.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.dispatch.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

July 24, 2017

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/bundles/config/org.eclipse.smarthome.config.dispatch.test/build.properties b/bundles/config/org.eclipse.smarthome.config.dispatch.test/build.properties index 3f16129255e..71f608e525e 100644 --- a/bundles/config/org.eclipse.smarthome.config.dispatch.test/build.properties +++ b/bundles/config/org.eclipse.smarthome.config.dispatch.test/build.properties @@ -1,5 +1,6 @@ bin.includes = .,\ META-INF/,\ - OSGI-INF/ + OSGI-INF/,\ + about.html source.. = src/test/groovy/ output.. = target/classes/ diff --git a/bundles/config/org.eclipse.smarthome.config.dispatch/about.html b/bundles/config/org.eclipse.smarthome.config.dispatch/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/bundles/config/org.eclipse.smarthome.config.dispatch/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

July 24, 2017

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/bundles/config/org.eclipse.smarthome.config.dispatch/build.properties b/bundles/config/org.eclipse.smarthome.config.dispatch/build.properties index 4cde906f762..9beae6ace39 100644 --- a/bundles/config/org.eclipse.smarthome.config.dispatch/build.properties +++ b/bundles/config/org.eclipse.smarthome.config.dispatch/build.properties @@ -2,4 +2,5 @@ source.. = src/main/java/ output.. = target/classes/ bin.includes = META-INF/,\ .,\ - OSGI-INF/ + OSGI-INF/,\ + about.html diff --git a/bundles/config/org.eclipse.smarthome.config.xml/META-INF/MANIFEST.MF b/bundles/config/org.eclipse.smarthome.config.xml/META-INF/MANIFEST.MF index c30796b6423..63bf67725d0 100644 --- a/bundles/config/org.eclipse.smarthome.config.xml/META-INF/MANIFEST.MF +++ b/bundles/config/org.eclipse.smarthome.config.xml/META-INF/MANIFEST.MF @@ -8,8 +8,14 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.thoughtworks.xstream, com.thoughtworks.xstream.annotations, com.thoughtworks.xstream.converters, + com.thoughtworks.xstream.converters.basic, + com.thoughtworks.xstream.converters.reflection, + com.thoughtworks.xstream.core, + com.thoughtworks.xstream.core.util, com.thoughtworks.xstream.io, + com.thoughtworks.xstream.io.naming, com.thoughtworks.xstream.io.xml, + com.thoughtworks.xstream.mapper, javax.security.auth, javax.xml.namespace, javax.xml.stream, diff --git a/bundles/config/org.eclipse.smarthome.config.xml/config-description-1.0.0.xsd b/bundles/config/org.eclipse.smarthome.config.xml/config-description-1.0.0.xsd index 91048f6d5c9..b1684376d03 100644 --- a/bundles/config/org.eclipse.smarthome.config.xml/config-description-1.0.0.xsd +++ b/bundles/config/org.eclipse.smarthome.config.xml/config-description-1.0.0.xsd @@ -123,39 +123,40 @@ - + - - - - - - - - + - - - - - + - + + + - - + + + + + - + + + + + + + + diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/about.html b/bundles/core/org.eclipse.smarthome.core.audio.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

July 24, 2017

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/build.properties b/bundles/core/org.eclipse.smarthome.core.audio.test/build.properties index 9ebf2ba45c2..1f6c66a9b22 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/build.properties +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/build.properties @@ -1,4 +1,5 @@ source.. = src/test/groovy/ output.. = target/classes/ bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/bundles/core/org.eclipse.smarthome.core.audio/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.audio/META-INF/MANIFEST.MF index 07f552ee5cc..6fe4da3f8e1 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.audio/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: javax.servlet, org.apache.commons.lang, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.audio, + org.eclipse.smarthome.core.audio.utils, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.i18n, @@ -25,6 +26,7 @@ Import-Package: javax.servlet, org.osgi.framework, org.osgi.service.http, org.slf4j -Export-Package: org.eclipse.smarthome.core.audio +Export-Package: org.eclipse.smarthome.core.audio, + org.eclipse.smarthome.core.audio.utils Service-Component: OSGI-INF/*.xml Bundle-ActivationPolicy: lazy diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java index d3d5693b2f5..a0a5f9e8277 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java +++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java @@ -14,19 +14,22 @@ import java.io.InputStream; import org.apache.commons.io.IOUtils; +import org.eclipse.smarthome.core.audio.utils.AudioStreamUtils; /** * This is an AudioStream from an audio file * * @author Karel Goderis - Initial contribution and API * @author Kai Kreuzer - Refactored to take a file as input + * @author Christoph Weitkamp - Refactored use of filename extension + * */ public class FileAudioStream extends FixedLengthAudioStream { - public static String WAV_EXTENSION = ".wav"; - public static String MP3_EXTENSION = ".mp3"; - public static String OGG_EXTENSION = ".ogg"; - public static String AAC_EXTENSION = ".aac"; + public static final String WAV_EXTENSION = "wav"; + public static final String MP3_EXTENSION = "mp3"; + public static final String OGG_EXTENSION = "ogg"; + public static final String AAC_EXTENSION = "aac"; private File file; private AudioFormat audioFormat; @@ -45,16 +48,20 @@ public FileAudioStream(File file, AudioFormat format) throws AudioException { } private static AudioFormat getAudioFormat(File file) throws AudioException { - if (file.getName().toLowerCase().endsWith(WAV_EXTENSION)) { - return new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 705600, 44100L); - } else if (file.getName().toLowerCase().endsWith(MP3_EXTENSION)) { - return AudioFormat.MP3; - } else if (file.getName().toLowerCase().endsWith(OGG_EXTENSION)) { - return AudioFormat.OGG; - } else if (file.getName().toLowerCase().endsWith(AAC_EXTENSION)) { - return AudioFormat.AAC; - } else { - throw new AudioException("Unsupported file extension!"); + final String filename = file.getName().toLowerCase(); + final String extension = AudioStreamUtils.getExtension(filename); + switch (extension) { + case WAV_EXTENSION: + return new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, false, 16, 705600, + 44100L); + case MP3_EXTENSION: + return AudioFormat.MP3; + case OGG_EXTENSION: + return AudioFormat.OGG; + case AAC_EXTENSION: + return AudioFormat.AAC; + default: + throw new AudioException("Unsupported file extension!"); } } diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/URLAudioStream.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/URLAudioStream.java index abca372358e..148ec4493ba 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/URLAudioStream.java +++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/URLAudioStream.java @@ -18,6 +18,7 @@ import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; +import org.eclipse.smarthome.core.audio.utils.AudioStreamUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,15 +28,19 @@ * * @author Karel Goderis - Initial contribution and API * @author Kai Kreuzer - Refactored to not require a source + * @author Christoph Weitkamp - Refactored use of filename extension * */ -public class URLAudioStream extends org.eclipse.smarthome.core.audio.AudioStream { +public class URLAudioStream extends AudioStream { - private static final Pattern plsStreamPattern = Pattern.compile("^File[0-9]=(.+)$"); + private static final Pattern PLS_STREAM_PATTERN = Pattern.compile("^File[0-9]=(.+)$"); + + public static final String M3U_EXTENSION = "m3u"; + public static final String PLS_EXTENSION = "pls"; private final Logger logger = LoggerFactory.getLogger(URLAudioStream.class); - private AudioFormat audioFormat; + private final AudioFormat audioFormat; private final InputStream inputStream; private String url; @@ -51,32 +56,38 @@ public URLAudioStream(String url) throws AudioException { } private InputStream createInputStream() throws AudioException { + final String filename = url.toLowerCase(); + final String extension = AudioStreamUtils.getExtension(filename); try { - if (url.toLowerCase().endsWith(".m3u")) { - InputStream is = new URL(url).openStream(); - String urls = IOUtils.toString(is); - for (String line : urls.split("\n")) { - if (!line.isEmpty() && !line.startsWith("#")) { - url = line; - break; + switch (extension) { + case M3U_EXTENSION: + try (final InputStream isM3U = new URL(url).openStream()) { + for (final String line : IOUtils.readLines(isM3U)) { + if (!line.isEmpty() && !line.startsWith("#")) { + url = line; + break; + } + } } - } - } else if (url.toLowerCase().endsWith(".pls")) { - InputStream is = new URL(url).openStream(); - String urls = IOUtils.toString(is); - for (String line : urls.split("\n")) { - if (!line.isEmpty() && line.startsWith("File")) { - Matcher matcher = plsStreamPattern.matcher(line); - if (matcher.find()) { - url = matcher.group(1); - break; + break; + case PLS_EXTENSION: + try (final InputStream isPLS = new URL(url).openStream()) { + for (final String line : IOUtils.readLines(isPLS)) { + if (!line.isEmpty() && line.startsWith("File")) { + final Matcher matcher = PLS_STREAM_PATTERN.matcher(line); + if (matcher.find()) { + url = matcher.group(1); + break; + } + } } } - } + break; + default: + break; } URL streamUrl = new URL(url); URLConnection connection = streamUrl.openConnection(); - InputStream is = null; if (connection.getContentType().equals("unknown/unknown")) { // Java does not parse non-standard headers used by SHOUTCast int port = streamUrl.getPort() > 0 ? streamUrl.getPort() : 80; @@ -88,19 +99,18 @@ private InputStream createInputStream() throws AudioException { String req = "GET / HTTP/1.0\r\nuser-agent: " + user_agent + "\r\nIcy-MetaData: 1\r\nConnection: keep-alive\r\n\r\n"; os.write(req.getBytes()); - is = shoutCastSocket.getInputStream(); + return shoutCastSocket.getInputStream(); } else { // getInputStream() method is more error-proof than openStream(), // because openStream() does openConnection().getInputStream(), // which opens a new connection and does not reuse the old one. - is = connection.getInputStream(); + return connection.getInputStream(); } - return is; } catch (MalformedURLException e) { - logger.error("URL '{}' is not a valid url : '{}'", url, e.getMessage()); + logger.error("URL '{}' is not a valid url: {}", url, e.getMessage(), e); throw new AudioException("URL not valid"); } catch (IOException e) { - logger.error("Cannot set up stream '{}': {}", url, e); + logger.error("Cannot set up stream '{}': {}", url, e.getMessage(), e); throw new AudioException("IO Error"); } } diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/utils/AudioStreamUtils.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/utils/AudioStreamUtils.java new file mode 100644 index 00000000000..f3fb19bab81 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/utils/AudioStreamUtils.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.core.audio.utils; + +/** + * Some general filename and extension utilities. + * + * @author Christoph Weitkamp - Initial contribution and API + * + */ +public class AudioStreamUtils { + + public static final String EXTENSION_SEPARATOR = "."; + + /** + * Gets the base name of a filename. + * + * @param filename the filename to query + * @return the base name of the file or an empty string if none exists or {@code null} if the filename is + * {@code null} + */ + public static String getBaseName(String filename) { + if (filename == null) { + return null; + } + final int index = filename.lastIndexOf(EXTENSION_SEPARATOR); + if (index == -1) { + return filename; + } else { + return filename.substring(0, index); + } + } + + /** + * Gets the extension of a filename. + * + * @param filename the filename to retrieve the extension of + * @return the extension of the file or an empty string if none exists or {@code null} if the filename is + * {@code null} + */ + public static String getExtension(String filename) { + if (filename == null) { + return null; + } + final int index = filename.lastIndexOf(EXTENSION_SEPARATOR); + if (index == -1) { + return ""; + } else { + return filename.substring(index + 1); + } + } + + /** + * Checks if the extension of a filename matches the given. + * + * @param filename the filename to check the extension of + * @param extension the extension to check for + * @return {@code true} if the filename has the specified extension + */ + public static boolean isExtension(String filename, String extension) { + if (filename == null) { + return false; + } + if (extension == null || extension.isEmpty()) { + return false; + } + return getExtension(filename).equals(extension); + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.binding.xml.test/src/test/groovy/org/eclipse/smarthome/core/binding/xml/test/BindingInfoI18nTest.groovy b/bundles/core/org.eclipse.smarthome.core.binding.xml.test/src/test/groovy/org/eclipse/smarthome/core/binding/xml/test/BindingInfoI18nTest.groovy index 58194680500..d77d3b282b5 100644 --- a/bundles/core/org.eclipse.smarthome.core.binding.xml.test/src/test/groovy/org/eclipse/smarthome/core/binding/xml/test/BindingInfoI18nTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.binding.xml.test/src/test/groovy/org/eclipse/smarthome/core/binding/xml/test/BindingInfoI18nTest.groovy @@ -13,6 +13,7 @@ import static org.junit.matchers.JUnitMatchers.* import org.eclipse.smarthome.core.binding.BindingInfo import org.eclipse.smarthome.core.binding.BindingInfoRegistry +import org.eclipse.smarthome.core.i18n.LocaleProvider import org.eclipse.smarthome.test.OSGiTest import org.eclipse.smarthome.test.SyntheticBundleInstaller import org.junit.After @@ -109,6 +110,12 @@ class BindingInfoI18nTest extends OSGiTest { localeCfg.put("country", "DE"); config.update(localeCfg); + //before running the test with a default locale make sure the locale has been set + LocaleProvider localeProvider = getService(LocaleProvider.class); + waitForAssert { + assertThat localeProvider.getLocale().toString(), is("de") + } + def bundleContext = getBundleContext() def initialNumberOfBindingInfos = bindingInfoRegistry.getBindingInfos().size() diff --git a/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF index 3d423a0965a..16f52f68702 100644 --- a/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.persistence/META-INF/MANIFEST.MF @@ -6,10 +6,14 @@ Bundle-Version: 0.9.0.qualifier Bundle-ManifestVersion: 2 Bundle-License: http://www.eclipse.org/legal/epl-v10.html Bundle-SymbolicName: org.eclipse.smarthome.core.persistence -Import-Package: org.eclipse.smarthome.config.core, +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.items, org.eclipse.smarthome.core.persistence, + org.eclipse.smarthome.core.persistence.config, + org.eclipse.smarthome.core.persistence.dto, + org.eclipse.smarthome.core.persistence.strategy, org.eclipse.smarthome.core.scheduler, org.eclipse.smarthome.core.types, org.slf4j diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy index 741a93f8111..94b7018c96d 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy @@ -396,7 +396,6 @@ class AbstractWatchServiceTest extends OSGiTest { allFullEvents << fullEvent } - @SuppressWarnings("unchecked") @Override protected Kind[] getWatchEventKinds(Path subDir) { return [ diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/storage/ManagedItemProviderOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/storage/ManagedItemProviderOSGiTest.groovy index 8342bee77d1..8605ecb53b5 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/storage/ManagedItemProviderOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/storage/ManagedItemProviderOSGiTest.groovy @@ -21,11 +21,11 @@ import org.eclipse.smarthome.core.items.ManagedItemProvider.PersistedItem import org.eclipse.smarthome.core.library.items.NumberItem import org.eclipse.smarthome.core.library.items.StringItem import org.eclipse.smarthome.core.library.items.SwitchItem +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType import org.eclipse.smarthome.core.library.types.ArithmeticGroupFunction.And import org.eclipse.smarthome.core.library.types.ArithmeticGroupFunction.Avg import org.eclipse.smarthome.core.library.types.ArithmeticGroupFunction.Sum -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.library.types.StringType import org.eclipse.smarthome.core.types.Command import org.eclipse.smarthome.core.types.State import org.eclipse.smarthome.test.OSGiTest @@ -216,7 +216,7 @@ class ManagedItemProviderOSGiTest extends OSGiTest { Storage storage = storageService.getStorage(Item.class.getName()) StrangeItem item = new StrangeItem('SomeStrangeItem') - String key = itemProvider.keyToString(itemProvider.getKey(item)) + String key = itemProvider.keyToString(item.getUID()) // put an item into the storage that cannot be handled (yet) PersistedItem persistableElement = storage.put(key, itemProvider.toPersistableElement(item)) @@ -256,8 +256,8 @@ class ManagedItemProviderOSGiTest extends OSGiTest { GroupItem groupItem = new GroupItem('SomeGroupItem') item.addGroupName(groupItem.getName()) groupItem.addMember(item) - String itemKey = itemProvider.keyToString(itemProvider.getKey(item)) - String groupKey = itemProvider.keyToString(itemProvider.getKey(groupItem)) + String itemKey = itemProvider.keyToString(item.getUID()) + String groupKey = itemProvider.keyToString(groupItem.getUID()) // put items into the storage that cannot be handled (yet) PersistedItem persistableElement1 = storage.put(itemKey, itemProvider.toPersistableElement(item)) @@ -316,7 +316,7 @@ class ManagedItemProviderOSGiTest extends OSGiTest { @Test - void 'assert group functions are stored and retrieved as well'() { + void 'assert group functions are stored and retrieved as well'() { assertThat itemProvider.getAll().size(), is(0) diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/GroupItemTest.java b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/GroupItemTest.java index 9d33ba2e41a..d2edf474f72 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/GroupItemTest.java +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/GroupItemTest.java @@ -10,14 +10,18 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.events.EventFilter; import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.events.EventSubscriber; import org.eclipse.smarthome.core.items.events.GroupItemStateChangedEvent; +import org.eclipse.smarthome.core.items.events.ItemUpdatedEvent; import org.eclipse.smarthome.core.library.items.ColorItem; import org.eclipse.smarthome.core.library.items.DimmerItem; import org.eclipse.smarthome.core.library.items.NumberItem; @@ -33,6 +37,7 @@ import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.test.java.JavaOSGiTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; public class GroupItemTest extends JavaOSGiTest { @@ -43,9 +48,61 @@ public class GroupItemTest extends JavaOSGiTest { List events = new LinkedList<>(); EventPublisher publisher; + ItemRegistry itemRegistry; + @Before public void setUp() { + registerVolatileStorageService(); publisher = event -> events.add(event); + + itemRegistry = getService(ItemRegistry.class); + assertNotNull(itemRegistry); + + registerService(new EventSubscriber() { + + @Override + public void receive(Event event) { + events.add(event); + } + + @Override + public Set getSubscribedEventTypes() { + HashSet hs = new HashSet<>(); + hs.add(ItemUpdatedEvent.TYPE); + return hs; + } + + @Override + public EventFilter getEventFilter() { + return null; + } + }); + } + + @Ignore + @Test + public void testItemUpdateWithItemRegistry() { + GroupItem item = new GroupItem("mySimpleGroupItem"); + item.setLabel("firstLabel"); + + itemRegistry.add(item); + + GroupItem updatedItem = (GroupItem) itemRegistry.get("mySimpleGroupItem"); + assertNotNull(updatedItem); + + events.clear(); + + updatedItem.setLabel("secondLabel"); + itemRegistry.update(updatedItem); + waitForAssert(() -> assertThat(events.size(), is(1))); + + List stateChanges = events.stream().filter(it -> it instanceof ItemUpdatedEvent) + .collect(Collectors.toList()); + assertThat(stateChanges.size(), is(1)); + + ItemUpdatedEvent change = (ItemUpdatedEvent) stateChanges.get(0); + + assertThat(change.getItem().label, is("secondLabel")); } @Test() diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/net/NetUtilTest.java b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/net/NetUtilTest.java new file mode 100644 index 00000000000..3eb68a36999 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/net/NetUtilTest.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.core.net; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +/** + * Tests for NetUtil class + * + * @author Stefan Triller - initial contribution + * + */ +public class NetUtilTest { + + @Test + public void testNetwork() { + String network = NetUtil.getIpv4NetAddress("192.168.0.1", (short) 24); + assertThat(network, is("192.168.0.0")); + + network = NetUtil.getIpv4NetAddress("192.168.23.5", (short) 24); + assertThat(network, is("192.168.23.0")); + + network = NetUtil.getIpv4NetAddress("172.16.42.23", (short) 16); + assertThat(network, is("172.16.0.0")); + + network = NetUtil.getIpv4NetAddress("10.8.13.5", (short) 8); + assertThat(network, is("10.0.0.0")); + + network = NetUtil.getIpv4NetAddress("192.168.5.8", (short) 23); + assertThat(network, is("192.168.4.0")); + + network = NetUtil.getIpv4NetAddress("192.168.5.8", (short) 27); + assertThat(network, is("192.168.5.0")); + + network = NetUtil.getIpv4NetAddress("192.168.5.8", (short) 29); + assertThat(network, is("192.168.5.8")); + + try { + network = NetUtil.getIpv4NetAddress("192.168.5.8", (short) 32); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage(), is("Netmask '32' is out of bounds (1-31)")); + } + try { + network = NetUtil.getIpv4NetAddress("192.168.58", (short) 24); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage(), is("IP '192.168.58' is not a valid IPv4 address")); + } + try { + network = NetUtil.getIpv4NetAddress("SOME_TEXT", (short) 24); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage(), is("IP 'SOME_TEXT' is not a valid IPv4 address")); + } + try { + network = NetUtil.getIpv4NetAddress("SOME_TEXT", (short) 42); + } catch (IllegalArgumentException iae) { + assertThat(iae.getMessage(), is("IP 'SOME_TEXT' is not a valid IPv4 address")); + } + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/scheduler/CronExpressionTest.java b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/scheduler/CronExpressionTest.java index bf2982082e3..3b90d6ef719 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/scheduler/CronExpressionTest.java +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/scheduler/CronExpressionTest.java @@ -17,6 +17,7 @@ import java.util.List; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,6 +79,8 @@ public void getFinalTimeCheck() throws ParseException { } @Test + @Ignore + // FIXME: see issue #3912 public void findNext() throws ParseException { boolean trace = false; @@ -115,11 +118,11 @@ public void findNext() throws ParseException { if (nextDate == null) { final String msg = String.format("Cannot find a time after '%s' for expression '%s'", sdf.format(curDate), cronExpression.getExpression()); - logger.error(msg); + logger.error("{}", msg); Assert.fail(msg); } else { if (trace) { - logger.info("Got: " + sdf.format(nextDate)); + logger.info("Got: {}", sdf.format(nextDate)); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/BindingBaseClassesOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/BindingBaseClassesOSGiTest.groovy index 87a9d613648..26c5396101d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/BindingBaseClassesOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/BindingBaseClassesOSGiTest.groovy @@ -116,6 +116,11 @@ class BindingBaseClassesOSGiTest extends OSGiTest { // check getBridge works assertThat getBridge().getUID().toString(), is("bindingId:type1:bridgeId") } + + @Override + void initialize() { + updateStatus(ThingStatus.ONLINE); + } } class SimpleBridgeHandler extends BaseBridgeHandler { @@ -131,6 +136,11 @@ class BindingBaseClassesOSGiTest extends OSGiTest { public void updateBridgetatus(ThingStatus status) { updateStatus(status) } + + @Override + void initialize() { + updateStatus(ThingStatus.ONLINE); + } } @@ -314,6 +324,11 @@ class BindingBaseClassesOSGiTest extends OSGiTest { // not implemented } + @Override + void initialize() { + updateStatus(ThingStatus.ONLINE); + } + @Override public Collection getConfigStatus() { if("invalid".equals(getThing().getConfiguration().get(PARAM))) { @@ -344,12 +359,12 @@ class BindingBaseClassesOSGiTest extends OSGiTest { @Override public void initialize() { - super.initialize() ThingBuilder thingBuilder = editThing() thingBuilder.withChannels([ new Channel(new ChannelUID("bindingId:type:thingId:1"), "String") ]) updateThing(thingBuilder.build()) + updateStatus(ThingStatus.ONLINE) } @Override diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ChangeThingTypeOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ChangeThingTypeOSGiTest.groovy index 1a6ef3f22a7..1ac3a4ecc7d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ChangeThingTypeOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ChangeThingTypeOSGiTest.groovy @@ -209,7 +209,7 @@ class ChangeThingTypeOSGiTest extends OSGiTest { @Override public void initialize() { println "[ChangeThingTypeOSGiTest] GenericThingHandler.initialize" - super.initialize() + updateStatus(ThingStatus.ONLINE) genericInits++; if (selfChanging) { changeThingType(THING_TYPE_SPECIFIC_UID, new Configuration(['providedspecific':'there'])) @@ -233,7 +233,7 @@ class ChangeThingTypeOSGiTest extends OSGiTest { public void initialize() { println "[ChangeThingTypeOSGiTest] SpecificThingHandler.initialize" specificInits++; - super.initialize(); + updateStatus(ThingStatus.ONLINE); } @Override diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ThingFactoryTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ThingFactoryTest.groovy index b98bc940216..b6cc5104900 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ThingFactoryTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/binding/ThingFactoryTest.groovy @@ -181,8 +181,8 @@ class ThingFactoryTest extends OSGiTest{ ChannelDefinition channelDef1 = new ChannelDefinition("ch1", channelType1.UID) ChannelDefinition channelDef2 = new ChannelDefinition("ch2", channelType2.UID) - ChannelGroupType channelGroupType1 = new ChannelGroupType(new ChannelGroupTypeUID("bindingid:groupTypeId1"), false, "label", "description", [channelDef1, channelDef2]) - ChannelGroupType channelGroupType2 = new ChannelGroupType(new ChannelGroupTypeUID("bindingid:groupTypeId2"), false, "label", "description", [channelDef1]) + ChannelGroupType channelGroupType1 = new ChannelGroupType(new ChannelGroupTypeUID("bindingid:groupTypeId1"), false, "label", "description", "myCategory1", [channelDef1, channelDef2]) + ChannelGroupType channelGroupType2 = new ChannelGroupType(new ChannelGroupTypeUID("bindingid:groupTypeId2"), false, "label", "description", "myCategory2", [channelDef1]) ChannelGroupDefinition channelGroupDef1 = new ChannelGroupDefinition("group1", channelGroupType1.UID) ChannelGroupDefinition channelGroupDef2 = new ChannelGroupDefinition("group2", channelGroupType2.UID) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateServiceOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateServiceOSGiTest.groovy index ef751749506..23e4a09d126 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateServiceOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateServiceOSGiTest.groovy @@ -210,7 +210,7 @@ final class FirmwareUpdateServiceOSGiTest extends OSGiTest { managedThingProvider = getService ManagedThingProvider assertThat managedThingProvider, is(notNullValue()) } - + thingRegistry = getService(ThingRegistry) assertThat thingRegistry, is(notNullValue()) @@ -1099,7 +1099,7 @@ final class FirmwareUpdateServiceOSGiTest extends OSGiTest { @Override public void initialize() { sleep wait - super.initialize(); + updateStatus(ThingStatus.ONLINE) } @Override @@ -1172,7 +1172,7 @@ final class FirmwareUpdateServiceOSGiTest extends OSGiTest { @Override public void initialize() { sleep wait - super.initialize() + updateStatus(ThingStatus.ONLINE) } @Override @@ -1223,5 +1223,10 @@ final class FirmwareUpdateServiceOSGiTest extends OSGiTest { @Override public void handleCommand(ChannelUID channelUID, Command command) { } + + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE) + } } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/i18n/ThingStatusInfoI18nLocalizationServiceOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/i18n/ThingStatusInfoI18nLocalizationServiceOSGiTest.groovy index a53c61ab29c..b53d67dffb7 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/i18n/ThingStatusInfoI18nLocalizationServiceOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/i18n/ThingStatusInfoI18nLocalizationServiceOSGiTest.groovy @@ -201,6 +201,11 @@ class ThingStatusInfoI18nLocalizationServiceOSGiTest extends OSGiTest { //do nothing } + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE); + } + void setThingStatusInfo(ThingStatusInfo thingStatusInfo) { updateStatus(thingStatusInfo.getStatus(), thingStatusInfo.getStatusDetail(), thingStatusInfo.getDescription()) } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/internal/ThingLinkManagerOSGiTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/internal/ThingLinkManagerOSGiTest.groovy index b1361ef88d7..ded05e89b71 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/internal/ThingLinkManagerOSGiTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/groovy/org/eclipse/smarthome/core/thing/internal/ThingLinkManagerOSGiTest.groovy @@ -20,6 +20,7 @@ import org.eclipse.smarthome.core.thing.ChannelUID import org.eclipse.smarthome.core.thing.ManagedThingProvider import org.eclipse.smarthome.core.thing.Thing import org.eclipse.smarthome.core.thing.ThingRegistry +import org.eclipse.smarthome.core.thing.ThingStatus import org.eclipse.smarthome.core.thing.ThingTypeUID import org.eclipse.smarthome.core.thing.ThingUID import org.eclipse.smarthome.core.thing.binding.BaseThingHandler @@ -176,6 +177,11 @@ class ThingLinkManagerOSGiTest extends OSGiTest { return new BaseThingHandler(thing) { public void handleCommand(ChannelUID channelUID, Command command) { } + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE); + } + void channelLinked(ChannelUID channelUID) { putContext("linkedChannel", channelUID) } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/ChannelStateDescriptionProviderOSGiTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/ChannelStateDescriptionProviderOSGiTest.java index 8010eae1763..d373611da7b 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/ChannelStateDescriptionProviderOSGiTest.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/ChannelStateDescriptionProviderOSGiTest.java @@ -32,6 +32,7 @@ import org.eclipse.smarthome.core.thing.ManagedThingProvider; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingRegistry; +import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; @@ -296,6 +297,11 @@ protected ThingHandler createHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { } + + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE); + } }; } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/CoreThingXmlTests.launch b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/CoreThingXmlTests.launch index 1d6daa7f522..209ff888343 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/CoreThingXmlTests.launch +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/CoreThingXmlTests.launch @@ -25,7 +25,7 @@ - + @@ -33,7 +33,7 @@ - + diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ChannelTypesTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ChannelTypesTest.groovy index 6632fa58cf7..d6fd38e90df 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ChannelTypesTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ChannelTypesTest.groovy @@ -74,6 +74,7 @@ class ChannelTypesTest extends OSGiTest { def channelGroupType = channelGroupTypes.find( {it.UID.toString().equals("somebinding:channelgroup")} ) assertThat channelGroupType, is(not(null)) + assertThat channelGroupType.category, is("Temperature") SyntheticBundleInstaller.uninstall(bundleContext, TEST_BUNDLE_NAME) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ThingTypesTest.groovy b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ThingTypesTest.groovy index eba7f9050ff..ce2138ceb0e 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ThingTypesTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/groovy/org/eclipse/smarthome/core/thing/xml/test/ThingTypesTest.groovy @@ -59,6 +59,7 @@ class ThingTypesTest extends OSGiTest { assertThat bridgeType.description, is("The hue Bridge represents the Philips hue bridge.") assertThat bridgeType.properties.size(), is(1) assertThat bridgeType.properties.get("vendor"), is("Philips") + assertThat bridgeType.representationProperty, is("serialNumber") def thingType = thingTypes.find { it.toString().equals("hue:lamp") } as ThingType assertThat thingType, is(notNullValue()) @@ -70,6 +71,7 @@ class ThingTypesTest extends OSGiTest { assertThat thingType.properties.size(), is(2) assertThat thingType.properties.get("key1"), is("value1") assertThat thingType.properties.get("key2"), is("value2") + assertThat thingType.representationProperty, is("uniqueId") thingType.channelDefinitions.with { assertThat size(), is(3) def colorChannel = it.find { it.id.equals("color") } as ChannelDefinition diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ChannelTypesTest.bundle/ESH-INF/thing/thing-types.xml b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ChannelTypesTest.bundle/ESH-INF/thing/thing-types.xml index 509b6e017d0..88ef35a087c 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ChannelTypesTest.bundle/ESH-INF/thing/thing-types.xml +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ChannelTypesTest.bundle/ESH-INF/thing/thing-types.xml @@ -26,6 +26,7 @@ + Temperature diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ThingTypesTest.bundle/ESH-INF/thing/thing-types.xml b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ThingTypesTest.bundle/ESH-INF/thing/thing-types.xml index 252ae675331..6a9dfbee1c7 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ThingTypesTest.bundle/ESH-INF/thing/thing-types.xml +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml.test/src/test/resources/test-bundle-pool/ThingTypesTest.bundle/ESH-INF/thing/thing-types.xml @@ -13,6 +13,7 @@ Philips + serialNumber @@ -55,6 +56,7 @@ value1 value2 + uniqueId diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeConverter.java index 08b3a0bc7cf..37c7b415e92 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeConverter.java @@ -27,6 +27,7 @@ * * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added thing and thing type properties + * @author Andre Fuechsel - Added representationProperty */ public class BridgeTypeConverter extends ThingTypeConverter { @@ -43,7 +44,7 @@ protected BridgeTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Unm super.readSupportedBridgeTypeUIDs(nodeIterator, context), super.readLabel(nodeIterator), super.readDescription(nodeIterator), super.getListed(attributes), super.getChannelTypeReferenceObjects(nodeIterator), getProperties(nodeIterator), - super.getConfigDescriptionObjects(nodeIterator)); + super.getRepresentationProperty(nodeIterator), super.getConfigDescriptionObjects(nodeIterator)); return bridgeTypeXmlResult; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeXmlResult.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeXmlResult.java index 5bc7613681e..a1f8bfbfa2c 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeXmlResult.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/BridgeTypeXmlResult.java @@ -26,22 +26,24 @@ * * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added thing and thing type properties + * @author Andre Fuechsel - Added representationProperty */ public class BridgeTypeXmlResult extends ThingTypeXmlResult { public BridgeTypeXmlResult(ThingTypeUID bridgeTypeUID, List supportedBridgeTypeUIDs, String label, String description, boolean listed, List[] channelTypeReferenceObjects, - List properties, Object[] configDescriptionObjects) { + List properties, String representationProperty, Object[] configDescriptionObjects) { super(bridgeTypeUID, supportedBridgeTypeUIDs, label, description, listed, channelTypeReferenceObjects, - properties, configDescriptionObjects); + properties, representationProperty, configDescriptionObjects); } @Override public ThingType toThingType() throws ConversionException { BridgeType bridgeType = new BridgeType(super.thingTypeUID, super.supportedBridgeTypeUIDs, super.label, - super.description, super.listed, super.toChannelDefinitions(this.channelTypeReferences), + super.description, super.listed, super.representationProperty, + super.toChannelDefinitions(this.channelTypeReferences), super.toChannelGroupDefinitions(this.channelGroupTypeReferences), super.toPropertiesMap(), super.configDescriptionURI); @@ -52,9 +54,10 @@ public ThingType toThingType() throws ConversionException { public String toString() { return "BridgeTypeXmlResult [thingTypeUID=" + thingTypeUID + ", supportedBridgeTypeUIDs=" + supportedBridgeTypeUIDs + ", label=" + label + ", description=" + description + ", listed=" + listed - + ", channelTypeReferences=" + channelTypeReferences + ", channelGroupTypeReferences=" - + channelGroupTypeReferences + ", properties=" + properties + ", configDescriptionURI=" - + configDescriptionURI + ", configDescription=" + configDescription + "]"; + + ", representationProperty=" + representationProperty + ", channelTypeReferences=" + + channelTypeReferences + ", channelGroupTypeReferences=" + channelGroupTypeReferences + ", properties=" + + properties + ", configDescriptionURI=" + configDescriptionURI + ", configDescription=" + + configDescription + "]"; } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeConverter.java index 945bdeb42b1..4bf62076580 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeConverter.java @@ -35,8 +35,8 @@ public class ChannelGroupTypeConverter extends AbstractDescriptionTypeConverter< public ChannelGroupTypeConverter() { super(ChannelGroupTypeXmlResult.class, "channel-group-type"); - super.attributeMapValidator = new ConverterAttributeMapValidator(new String[][] { { "id", "true" }, - { "advanced", "false" } }); + super.attributeMapValidator = new ConverterAttributeMapValidator( + new String[][] { { "id", "true" }, { "advanced", "false" } }); } private boolean isAdvanced(Map attributes, boolean defaultValue) { @@ -65,12 +65,22 @@ protected ChannelGroupTypeXmlResult unmarshalType(HierarchicalStreamReader reade String label = super.readLabel(nodeIterator); String description = super.readDescription(nodeIterator); + String category = readCategory(nodeIterator); List channelTypeDefinitions = readChannelTypeDefinitions(nodeIterator); - ChannelGroupTypeXmlResult groupChannelType = new ChannelGroupTypeXmlResult(channelGroupTypeUID, advanced, - label, description, channelTypeDefinitions); + ChannelGroupTypeXmlResult groupChannelType = new ChannelGroupTypeXmlResult(channelGroupTypeUID, advanced, label, + description, category, channelTypeDefinitions); return groupChannelType; } + private String readCategory(NodeIterator nodeIterator) { + Object category = nodeIterator.nextValue("category", false); + if (category != null) { + return category.toString(); + } else { + return null; + } + } + } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeXmlResult.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeXmlResult.java index 3b671e31801..e4792d7fdf4 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeXmlResult.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelGroupTypeXmlResult.java @@ -32,15 +32,17 @@ public class ChannelGroupTypeXmlResult { private boolean advanced; private String label; private String description; + private String category; private List channelTypeReferences; public ChannelGroupTypeXmlResult(ChannelGroupTypeUID channelGroupTypeUID, boolean advanced, String label, - String description, List channelTypeReferences) { + String description, String category, List channelTypeReferences) { this.channelGroupTypeUID = channelGroupTypeUID; this.advanced = advanced; this.label = label; this.description = description; + this.category = category; this.channelTypeReferences = channelTypeReferences; } @@ -67,7 +69,7 @@ protected List toChannelDefinitions(List ch public ChannelGroupType toChannelGroupType() throws ConversionException { ChannelGroupType channelGroupType = new ChannelGroupType(this.channelGroupTypeUID, this.advanced, this.label, - this.description, toChannelDefinitions(this.channelTypeReferences)); + this.description, this.category, toChannelDefinitions(this.channelTypeReferences)); return channelGroupType; } @@ -75,8 +77,8 @@ public ChannelGroupType toChannelGroupType() throws ConversionException { @Override public String toString() { return "ChannelGroupTypeXmlResult [channelGroupTypeUID=" + channelGroupTypeUID + ", advanced=" + advanced - + ", label=" + label + ", description=" + description + ", channelTypeReferences=" - + channelTypeReferences + "]"; + + ", label=" + label + ", description=" + description + ", category=" + category + + ", channelTypeReferences=" + channelTypeReferences + "]"; } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/EventDescriptionConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/EventDescriptionConverter.java index 1d3442e9f27..dd2a7c05fb2 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/EventDescriptionConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/EventDescriptionConverter.java @@ -10,12 +10,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; + import org.eclipse.smarthome.config.xml.util.GenericUnmarshaller; import org.eclipse.smarthome.config.xml.util.NodeIterator; import org.eclipse.smarthome.config.xml.util.NodeList; import org.eclipse.smarthome.config.xml.util.NodeValue; import org.eclipse.smarthome.core.types.EventDescription; import org.eclipse.smarthome.core.types.EventOption; + import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.UnmarshallingContext; @@ -26,6 +28,8 @@ * used to convert a event description within an XML document into a {@link EventDescription} object. *

* This converter converts {@code state} XML tags. + * + * @author Moritz Kammerer - Initial contribution */ public class EventDescriptionConverter extends GenericUnmarshaller { diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingDescriptionReader.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingDescriptionReader.java index d9778ae78cf..31b01dc1d03 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingDescriptionReader.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingDescriptionReader.java @@ -8,6 +8,7 @@ package org.eclipse.smarthome.core.thing.xml.internal; import java.util.List; + import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionParameterGroup; @@ -25,6 +26,7 @@ import org.eclipse.smarthome.config.xml.util.XmlDocumentReader; import org.eclipse.smarthome.core.types.EventDescription; import org.eclipse.smarthome.core.types.StateDescription; + import com.thoughtworks.xstream.XStream; /** @@ -101,6 +103,7 @@ public void registerAliases(XStream xstream) { xstream.alias("criteria", FilterCriteria.class); xstream.alias("properties", NodeList.class); xstream.alias("property", NodeValue.class); + xstream.alias("representation-property", NodeValue.class); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeConverter.java index f44ecd09eac..148feba5666 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeConverter.java @@ -32,6 +32,7 @@ * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added thing and thing type properties * @author Chris Jackson - Added channel properties + * @author Andre Fuechsel - Added representationProperty */ public class ThingTypeConverter extends AbstractDescriptionTypeConverter { @@ -95,7 +96,7 @@ protected ThingTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Unma new ThingTypeUID(super.getUID(attributes, context)), readSupportedBridgeTypeUIDs(nodeIterator, context), super.readLabel(nodeIterator), super.readDescription(nodeIterator), getListed(attributes), getChannelTypeReferenceObjects(nodeIterator), getProperties(nodeIterator), - super.getConfigDescriptionObjects(nodeIterator)); + getRepresentationProperty(nodeIterator), super.getConfigDescriptionObjects(nodeIterator)); return thingTypeXmlResult; } @@ -108,4 +109,8 @@ protected boolean getListed(Map attributes) { return true; } + protected String getRepresentationProperty(NodeIterator nodeIterator) { + return (String) nodeIterator.nextValue("representation-property", false); + } + } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeXmlResult.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeXmlResult.java index 7b5678d1403..70e44145ac4 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeXmlResult.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ThingTypeXmlResult.java @@ -35,6 +35,7 @@ * @author Thomas Höfer - Added thing and thing type properties * @author Chris Jackson - Added channel properties * @author Simon Kaufmann - Added listed field + * @author Andre Fuechsel - Added representationProperty field */ public class ThingTypeXmlResult { @@ -43,6 +44,7 @@ public class ThingTypeXmlResult { protected String label; protected String description; protected boolean listed; + protected String representationProperty; protected List channelTypeReferences; protected List channelGroupTypeReferences; protected List properties; @@ -51,13 +53,14 @@ public class ThingTypeXmlResult { public ThingTypeXmlResult(ThingTypeUID thingTypeUID, List supportedBridgeTypeUIDs, String label, String description, boolean listed, List[] channelTypeReferenceObjects, - List properties, Object[] configDescriptionObjects) { + List properties, String representationProperty, Object[] configDescriptionObjects) { this.thingTypeUID = thingTypeUID; this.supportedBridgeTypeUIDs = supportedBridgeTypeUIDs; this.label = label; this.description = description; this.listed = listed; + this.representationProperty = representationProperty; this.channelTypeReferences = channelTypeReferenceObjects[0]; this.channelGroupTypeReferences = channelTypeReferenceObjects[1]; this.properties = properties; @@ -126,7 +129,8 @@ protected Map toPropertiesMap() { public ThingType toThingType() throws ConversionException { ThingType thingType = new ThingType(this.thingTypeUID, this.supportedBridgeTypeUIDs, this.label, - this.description, this.listed, toChannelDefinitions(this.channelTypeReferences), + this.description, this.listed, this.representationProperty, + toChannelDefinitions(this.channelTypeReferences), toChannelGroupDefinitions(this.channelGroupTypeReferences), toPropertiesMap(), this.configDescriptionURI); @@ -137,9 +141,10 @@ this.description, this.listed, toChannelDefinitions(this.channelTypeReferences), public String toString() { return "ThingTypeXmlResult [thingTypeUID=" + thingTypeUID + ", supportedBridgeTypeUIDs=" + supportedBridgeTypeUIDs + ", label=" + label + ", description=" + description + ", listed=" + listed - + ", channelTypeReferences=" + channelTypeReferences + ", channelGroupTypeReferences=" - + channelGroupTypeReferences + ", properties=" + properties + ", configDescriptionURI=" - + configDescriptionURI + ", configDescription=" + configDescription + "]"; + + ", representationProperty=" + representationProperty + ", channelTypeReferences=" + + channelTypeReferences + ", channelGroupTypeReferences=" + channelGroupTypeReferences + ", properties=" + + properties + ", configDescriptionURI=" + configDescriptionURI + ", configDescription=" + + configDescription + "]"; } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/XmlChannelGroupTypeProvider.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/XmlChannelGroupTypeProvider.java index 0f59278422f..ac86af2a8e4 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/XmlChannelGroupTypeProvider.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/XmlChannelGroupTypeProvider.java @@ -67,7 +67,8 @@ protected ChannelGroupType localize(Bundle bundle, ChannelGroupType channelGroup channelGroupType.getDescription(), locale); ChannelGroupType localizedChannelGroupType = new ChannelGroupType(channelGroupTypeUID, - channelGroupType.isAdvanced(), label, description, channelGroupType.getChannelDefinitions()); + channelGroupType.isAdvanced(), label, description, channelGroupType.getCategory(), + channelGroupType.getChannelDefinitions()); return localizedChannelGroupType; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd b/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd index 0478d8798b2..74ae3d521d2 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd @@ -29,6 +29,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF index 88cae26aa28..a26257abd34 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF @@ -11,6 +11,7 @@ Import-Package: com.google.common.base, org.apache.commons.collections.iterators, org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.core.dto, org.eclipse.smarthome.config.core.status, @@ -28,8 +29,11 @@ Import-Package: com.google.common.base, org.eclipse.smarthome.core.thing, org.eclipse.smarthome.core.thing.binding, org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.binding.firmware, org.eclipse.smarthome.core.thing.dto, org.eclipse.smarthome.core.thing.events, + org.eclipse.smarthome.core.thing.firmware, + org.eclipse.smarthome.core.thing.firmware.dto, org.eclipse.smarthome.core.thing.i18n, org.eclipse.smarthome.core.thing.link, org.eclipse.smarthome.core.thing.link.dto, diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java index 1e2c9da7c29..1137a3a7b0c 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java @@ -13,6 +13,9 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.thing.type.ChannelKind; @@ -44,9 +47,9 @@ public class Channel { private String description; - private Configuration configuration; + private @NonNull Configuration configuration; - private Map properties; + private @NonNull Map<@NonNull String, String> properties; private Set defaultTags = new LinkedHashSet<>(); @@ -54,6 +57,8 @@ public class Channel { * Package protected default constructor to allow reflective instantiation. */ Channel() { + this.configuration = new Configuration(); + this.properties = Collections.unmodifiableMap(new HashMap<@NonNull String, String>(0)); } public Channel(ChannelUID uid, String acceptedItemType) { @@ -61,7 +66,7 @@ public Channel(ChannelUID uid, String acceptedItemType) { this.acceptedItemType = acceptedItemType; this.kind = ChannelKind.STATE; this.configuration = new Configuration(); - this.properties = Collections.unmodifiableMap(new HashMap(0)); + this.properties = Collections.unmodifiableMap(new HashMap<@NonNull String, String>(0)); } public Channel(ChannelUID uid, String acceptedItemType, Configuration configuration) { @@ -74,13 +79,13 @@ public Channel(ChannelUID uid, String acceptedItemType, Set defaultTags) } public Channel(ChannelUID uid, String acceptedItemType, Configuration configuration, Set defaultTags, - Map properties) { + Map<@NonNull String, String> properties) { this(uid, null, acceptedItemType, ChannelKind.STATE, null, defaultTags == null ? new HashSet(0) : defaultTags, properties, null, null); } public Channel(ChannelUID uid, ChannelTypeUID channelTypeUID, String acceptedItemType, ChannelKind kind, - Configuration configuration, Set defaultTags, Map properties, String label, + Configuration configuration, Set defaultTags, Map<@NonNull String, String> properties, String label, String description) { if (kind == null) { throw new IllegalArgumentException("kind must not be null"); @@ -89,17 +94,19 @@ public Channel(ChannelUID uid, ChannelTypeUID channelTypeUID, String acceptedIte this.uid = uid; this.channelTypeUID = channelTypeUID; this.acceptedItemType = acceptedItemType; - this.configuration = configuration; this.kind = kind; this.label = label; this.description = description; - this.properties = properties; this.defaultTags = Collections. unmodifiableSet(new HashSet(defaultTags)); - if (this.configuration == null) { + if (configuration == null) { this.configuration = new Configuration(); + } else { + this.configuration = configuration; } - if (this.properties == null) { - this.properties = Collections.unmodifiableMap(new HashMap(0)); + if (properties == null) { + this.properties = Collections.unmodifiableMap(new HashMap<@NonNull String, String>(0)); + } else { + this.properties = properties; } } @@ -140,7 +147,7 @@ public ChannelUID getUID() { * * @return channel type UID or null if no channel type is specified */ - public ChannelTypeUID getChannelTypeUID() { + public @Nullable ChannelTypeUID getChannelTypeUID() { return channelTypeUID; } @@ -150,7 +157,7 @@ public ChannelTypeUID getChannelTypeUID() { * * @return the label for the channel. Can be null. */ - public String getLabel() { + public @Nullable String getLabel() { return this.label; } @@ -161,7 +168,7 @@ public String getLabel() { * * @return the description for the channel. Can be null. */ - public String getDescription() { + public @Nullable String getDescription() { return this.description; } @@ -170,7 +177,7 @@ public String getDescription() { * * @return channel configuration (not null) */ - public Configuration getConfiguration() { + public @NonNull Configuration getConfiguration() { return configuration; } @@ -179,7 +186,7 @@ public Configuration getConfiguration() { * * @return channel properties (not null) */ - public Map getProperties() { + public @NonNull Map<@NonNull String, String> getProperties() { return properties; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ManagedThingProvider.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ManagedThingProvider.java index 82a98605ff5..7f6f467aacd 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ManagedThingProvider.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ManagedThingProvider.java @@ -21,11 +21,6 @@ */ public class ManagedThingProvider extends DefaultAbstractManagedProvider implements ThingProvider { - @Override - protected ThingUID getKey(Thing thing) { - return thing.getUID(); - } - @Override protected String getStorageName() { return Thing.class.getName(); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Thing.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Thing.java index d8c777f30ac..d8ea50b579a 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Thing.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Thing.java @@ -10,6 +10,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.common.registry.Identifiable; import org.eclipse.smarthome.core.items.Item; @@ -75,6 +77,7 @@ public interface Thing extends Identifiable { * @return the channel for the given id or null if no channel with the id * exists */ + @Nullable Channel getChannel(String channelId); /** @@ -117,6 +120,7 @@ public interface Thing extends Identifiable { * * @return the handler (can be null) */ + @Nullable ThingHandler getHandler(); /** @@ -124,6 +128,7 @@ public interface Thing extends Identifiable { * * @return the bridge UID (can be null) */ + @Nullable ThingUID getBridgeUID(); /** @@ -139,6 +144,7 @@ public interface Thing extends Identifiable { * * @return the configuration (not null) */ + @NonNull Configuration getConfiguration(); /** @@ -161,7 +167,8 @@ public interface Thing extends Identifiable { * * @return an immutable copy of the {@link Thing} properties (not null) */ - Map getProperties(); + @NonNull + Map<@NonNull String, String> getProperties(); /** * Sets the property value for the property identified by the given name. If the value to be set is null then the @@ -173,14 +180,14 @@ public interface Thing extends Identifiable { * * @return the previous value associated with the name, or null if there was no mapping for the name */ - String setProperty(String name, String value); + String setProperty(@NonNull String name, String value); /** * Updates all properties of the thing. * * @param properties the properties to set (must not be null) */ - void setProperties(Map properties); + void setProperties(@NonNull Map properties); /** * Get the physical location of the {@link Thing}. @@ -188,6 +195,7 @@ public interface Thing extends Identifiable { * @return the location identifier (presumably an item name) or null if no location has been * configured. */ + @Nullable String getLocation(); /** diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingRegistry.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingRegistry.java index 3dfeb688804..ca3a96dc815 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingRegistry.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingRegistry.java @@ -9,6 +9,8 @@ import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.validation.ConfigValidationException; import org.eclipse.smarthome.core.common.registry.Registry; @@ -42,7 +44,8 @@ public interface ThingRegistry extends Registry { * @param channelUID channel UID * @return channel for the given channel UID or null of no channel was found */ - Channel getChannel(ChannelUID channelUID); + @Nullable + Channel getChannel(@NonNull ChannelUID channelUID); /** * Updates the configuration of a thing for the given UID. @@ -53,7 +56,7 @@ public interface ThingRegistry extends Registry { * @throws ConfigValidationException if one or more of the given configuration parameters do not match * their declarations in the configuration description */ - void updateConfiguration(ThingUID thingUID, Map configurationParameters); + void updateConfiguration(@NonNull ThingUID thingUID, Map<@NonNull String, Object> configurationParameters); /** * Initiates the removal process for the {@link Thing} specified by the given {@link ThingUID}. @@ -80,7 +83,8 @@ public interface ThingRegistry extends Registry { * @param thingUID Identificator of the {@link Thing} to be removed * @return the {@link Thing} that was removed, or null if no {@link Thing} with the given {@link ThingUID} exists */ - Thing forceRemove(ThingUID thingUID); + @Nullable + Thing forceRemove(@NonNull ThingUID thingUID); /** * Creates a thing based on the given configuration properties @@ -97,6 +101,6 @@ public interface ThingRegistry extends Registry { * the configuration * @return the created thing */ - Thing createThingOfType(ThingTypeUID thingTypeUID, ThingUID thingUIDObject, ThingUID bridgeUID, String label, - Configuration configuration); + Thing createThingOfType(@NonNull ThingTypeUID thingTypeUID, ThingUID thingUIDObject, ThingUID bridgeUID, + String label, Configuration configuration); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingStatusInfo.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingStatusInfo.java index 8abfc050431..fe17f0c7c36 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingStatusInfo.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingStatusInfo.java @@ -8,6 +8,7 @@ package org.eclipse.smarthome.core.thing; import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNull; /** * A {@link ThingStatusInfo} represents status information of a thing which consists of @@ -22,9 +23,9 @@ */ public class ThingStatusInfo { - private ThingStatus status; + private @NonNull ThingStatus status; - private ThingStatusDetail statusDetail; + private @NonNull ThingStatusDetail statusDetail; private String description; @@ -32,6 +33,8 @@ public class ThingStatusInfo { * Default constructor for deserialization e.g. by Gson. */ protected ThingStatusInfo() { + status = ThingStatus.UNKNOWN; + statusDetail = ThingStatusDetail.NONE; } /** @@ -43,14 +46,8 @@ protected ThingStatusInfo() { * * @throws IllegalArgumentException if thing status or thing status detail is null */ - public ThingStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, String description) + public ThingStatusInfo(@NonNull ThingStatus status, @NonNull ThingStatusDetail statusDetail, String description) throws IllegalArgumentException { - if (status == null) { - throw new IllegalArgumentException("Thing status must not be null"); - } - if (statusDetail == null) { - throw new IllegalArgumentException("Thing status detail must not be null"); - } this.status = status; this.statusDetail = statusDetail; this.description = description; @@ -61,7 +58,7 @@ public ThingStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, Strin * * @return the status (not null) */ - public ThingStatus getStatus() { + public @NonNull ThingStatus getStatus() { return status; } @@ -70,7 +67,7 @@ public ThingStatus getStatus() { * * @return the status detail (not null) */ - public ThingStatusDetail getStatusDetail() { + public @NonNull ThingStatusDetail getStatusDetail() { return statusDetail; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingTypeMigrationService.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingTypeMigrationService.java index b7d06fcf2db..deb2332fc21 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingTypeMigrationService.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/ThingTypeMigrationService.java @@ -7,25 +7,26 @@ */ package org.eclipse.smarthome.core.thing; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; /** * The {@link ThingTypeMigrationService} describes a service to change the thing type * of a given {@link Thing}. - * + * * @author Andre Fuechsel - initial contribution */ public interface ThingTypeMigrationService { /** * Changes the type of a given {@link Thing}. - * + * * @param thing {@link Thing} whose type should be changed * @param thingTypeUID new {@link ThingTypeUID} * @param configuration new configuration - * + * * @throws RuntimeException, if the new thing type is not registered in the registry */ - void migrateThingType(Thing thing, ThingTypeUID thingTypeUID, Configuration configuration); + void migrateThingType(@NonNull Thing thing, @NonNull ThingTypeUID thingTypeUID, Configuration configuration); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseBridgeHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseBridgeHandler.java index 7f6a6ff2b58..e6e681d6f39 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseBridgeHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseBridgeHandler.java @@ -9,6 +9,7 @@ import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingUID; @@ -29,7 +30,7 @@ public abstract class BaseBridgeHandler extends BaseThingHandler implements Brid /** * @see BaseThingHandler */ - public BaseBridgeHandler(Bridge bridge) { + public BaseBridgeHandler(@NonNull Bridge bridge) { super(bridge); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandler.java index c53989b3e24..d4b2467315b 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandler.java @@ -12,6 +12,7 @@ import java.util.Map.Entry; import java.util.concurrent.ScheduledExecutorService; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.validation.ConfigDescriptionValidator; import org.eclipse.smarthome.config.core.validation.ConfigValidationException; @@ -41,8 +42,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; - /** * {@link BaseThingHandler} provides a base implementation for the {@link ThingHandler} interface. *

@@ -71,7 +70,7 @@ public abstract class BaseThingHandler implements ThingHandler { protected ItemChannelLinkRegistry linkRegistry; protected BundleContext bundleContext; - protected Thing thing; + protected @NonNull Thing thing; @SuppressWarnings("rawtypes") private ServiceTracker thingRegistryServiceTracker; @@ -84,11 +83,8 @@ public abstract class BaseThingHandler implements ThingHandler { * Creates a new instance of this class for the {@link Thing}. * * @param thing the thing that should be handled, not null - * - * @throws IllegalArgumentException if thing argument is null */ - public BaseThingHandler(Thing thing) { - Preconditions.checkArgument(thing != null, "The argument 'thing' must not be null."); + public BaseThingHandler(@NonNull Thing thing) { this.thing = thing; } @@ -168,7 +164,7 @@ public void dispose() { } @Override - public Thing getThing() { + public @NonNull Thing getThing() { return this.thing; } @@ -178,11 +174,12 @@ public void handleUpdate(ChannelUID channelUID, State newState) { } @Override + @Deprecated public void initialize() { - // can be overridden by subclasses - // standard behavior is to set the thing to ONLINE, - // assuming no further initialization is necessary. + // should be overridden by subclasses! updateStatus(ThingStatus.ONLINE); + logger.warn( + "BaseThingHandler.initialize() will be removed soon, ThingStatus can be set manually via updateStatus(ThingStatus.ONLINE)"); } @Override @@ -297,7 +294,7 @@ protected void triggerChannel(ChannelUID channelUID, String event) { if (this.callback != null) { this.callback.channelTriggered(this.getThing(), channelUID, event); } else { - throw new IllegalStateException("Could not update state, because callback is missing"); + throw new IllegalStateException("Could not trigger channel, because callback is missing"); } } } @@ -438,7 +435,7 @@ protected ThingBuilder editThing() { * @throws IllegalStateException * if handler is not initialized correctly, because no callback is present */ - protected void updateThing(Thing thing) { + protected void updateThing(@NonNull Thing thing) { synchronized (this) { if (this.callback != null) { this.thing = thing; @@ -532,7 +529,7 @@ protected void updateProperties(Map properties) { * @param name the name of the property to be set * @param value the value of the property */ - protected void updateProperty(String name, String value) { + protected void updateProperty(@NonNull String name, String value) { String existingPropertyValue = thing.getProperties().get(name); if (existingPropertyValue == null || !existingPropertyValue.equals(value)) { thing.setProperty(name, value); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandlerFactory.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandlerFactory.java index c536cd880b4..5ce91c62f85 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandlerFactory.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BaseThingHandlerFactory.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.status.ConfigStatusProvider; @@ -25,8 +27,6 @@ import org.osgi.service.component.ComponentContext; import org.osgi.util.tracker.ServiceTracker; -import com.google.common.base.Preconditions; - /** * The {@link BaseThingHandlerFactory} provides a base implementation for the {@link ThingHandlerFactory} interface. *

@@ -93,8 +93,6 @@ protected void deactivate(ComponentContext componentContext) { @Override public ThingHandler registerHandler(Thing thing) { - Preconditions.checkArgument(thing != null, "The argument 'thing' must not be null."); - ThingHandler thingHandler = createHandler(thing); if (thingHandler == null) { throw new IllegalStateException(this.getClass().getSimpleName() @@ -153,8 +151,6 @@ private ServiceRegistration registerAsService(ThingHandler thingHandler, @Override public void unregisterHandler(Thing thing) { - Preconditions.checkArgument(thing != null, "The argument 'thing' must not be null."); - ThingHandler thingHandler = thing.getHandler(); if (thingHandler != null) { removeHandler(thingHandler); @@ -172,7 +168,7 @@ public void unregisterHandler(Thing thing) { * @param thingHandler * thing handler to be removed */ - protected void removeHandler(ThingHandler thingHandler) { + protected void removeHandler(@NonNull ThingHandler thingHandler) { // can be overridden } @@ -209,7 +205,7 @@ public void removeThing(ThingUID thingUID) { * @param thingTypeUID the unique id of the thing type * @return the thing type represented by the given unique id */ - protected ThingType getThingTypeByUID(ThingTypeUID thingTypeUID) { + protected @Nullable ThingType getThingTypeByUID(@NonNull ThingTypeUID thingTypeUID) { if (thingTypeRegistryServiceTracker == null) { throw new IllegalStateException( "Base thing handler factory has not been properly initialized. Did you forget to call super.activate()?"); @@ -226,13 +222,14 @@ protected ThingType getThingTypeByUID(ThingTypeUID thingTypeUID) { * * @param thingTypeUID * thing type uid (can not be null) - * @param thingUID - * thingUID (can not be null) * @param configuration * (can not be null) + * @param thingUID + * thingUID (can not be null) * @return thing (can be null, if thing type is unknown) */ - protected Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID) { + protected @Nullable Thing createThing(@NonNull ThingTypeUID thingTypeUID, @NonNull Configuration configuration, + @NonNull ThingUID thingUID) { return createThing(thingTypeUID, configuration, thingUID, null); } @@ -252,9 +249,6 @@ protected Thing createThing(ThingTypeUID thingTypeUID, Configuration configurati @Override public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID, ThingUID bridgeUID) { - if (thingTypeUID == null) { - throw new IllegalArgumentException("Thing Type UID must not be null"); - } if (thingUID == null) { thingUID = ThingFactory.generateRandomThingUID(thingTypeUID); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BridgeHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BridgeHandler.java index fd3e3d809e2..fb65ef7d3ce 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BridgeHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/BridgeHandler.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.core.thing.binding; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; @@ -28,7 +29,7 @@ public interface BridgeHandler extends ThingHandler { * @param childHandler the initialized child handler * @param childThing the thing of the initialized child handler */ - void childHandlerInitialized(ThingHandler childHandler, Thing childThing); + void childHandlerInitialized(@NonNull ThingHandler childHandler, @NonNull Thing childThing); /** * Informs the bridge handler that a child handler has been disposed. @@ -36,6 +37,6 @@ public interface BridgeHandler extends ThingHandler { * @param childHandler the disposed child handler * @param childThing the thing of the disposed child handler */ - void childHandlerDisposed(ThingHandler childHandler, Thing childThing); + void childHandlerDisposed(@NonNull ThingHandler childHandler, @NonNull Thing childThing); } \ No newline at end of file diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusBridgeHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusBridgeHandler.java index 8dc22b4f3e4..0ebba009ac9 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusBridgeHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusBridgeHandler.java @@ -9,6 +9,7 @@ import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.status.ConfigStatusCallback; import org.eclipse.smarthome.config.core.status.ConfigStatusProvider; @@ -37,7 +38,7 @@ public abstract class ConfigStatusBridgeHandler extends BaseBridgeHandler implem * * @param bridge the bridge for this handler */ - public ConfigStatusBridgeHandler(Bridge bridge) { + public ConfigStatusBridgeHandler(@NonNull Bridge bridge) { super(bridge); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusThingHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusThingHandler.java index cf603aca363..78dfad1cac0 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusThingHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ConfigStatusThingHandler.java @@ -9,6 +9,7 @@ import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.status.ConfigStatusCallback; import org.eclipse.smarthome.config.core.status.ConfigStatusProvider; @@ -38,7 +39,7 @@ public abstract class ConfigStatusThingHandler extends BaseThingHandler implemen * * @param thing the thing for this handler */ - public ConfigStatusThingHandler(Thing thing) { + public ConfigStatusThingHandler(@NonNull Thing thing) { super(thing); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingConfigStatusSource.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingConfigStatusSource.java index 86b231e2fe9..b867c6dd5b0 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingConfigStatusSource.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingConfigStatusSource.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.core.thing.binding; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.status.ConfigStatusSource; import org.eclipse.smarthome.core.thing.Thing; @@ -24,7 +25,7 @@ public final class ThingConfigStatusSource extends ConfigStatusSource { * * @param thingUID the UID of the thing */ - public ThingConfigStatusSource(String thingUID) { + public ThingConfigStatusSource(@NonNull String thingUID) { super(thingUID); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingFactory.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingFactory.java index 96f14347daa..9154ab45b95 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingFactory.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingFactory.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.UUID; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; @@ -102,8 +103,9 @@ public static Thing createThing(ThingType thingType, ThingUID thingUID, Configur .withProperties(thingType.getProperties()).withBridge(bridgeUID).build(); } - public static Thing createThing(ThingUID thingUID, Configuration configuration, Map properties, - ThingUID bridgeUID, ThingTypeUID thingTypeUID, List thingHandlerFactories) { + public static Thing createThing(ThingUID thingUID, Configuration configuration, + Map<@NonNull String, String> properties, ThingUID bridgeUID, ThingTypeUID thingTypeUID, + List thingHandlerFactories) { for (ThingHandlerFactory thingHandlerFactory : thingHandlerFactories) { if (thingHandlerFactory.supportsThingType(thingTypeUID)) { Thing thing = thingHandlerFactory.createThing(thingTypeUID, configuration, thingUID, bridgeUID); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandler.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandler.java index 8baf8b62baf..b708ece253d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandler.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandler.java @@ -9,6 +9,7 @@ import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.validation.ConfigValidationException; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -88,7 +89,7 @@ public interface ThingHandler { * @param channelUID the {@link ChannelUID} of the channel to which the command was sent * @param command the {@link Command} */ - void handleCommand(ChannelUID channelUID, Command command); + void handleCommand(@NonNull ChannelUID channelUID, Command command); /** * Handles a {@link State} update for a given channel. @@ -99,7 +100,7 @@ public interface ThingHandler { * @param channelUID the {@link ChannelUID} of the channel on which the update was performed * @param newState the new {@link State} */ - void handleUpdate(ChannelUID channelUID, State newState); + void handleUpdate(@NonNull ChannelUID channelUID, @NonNull State newState); /** * Handles a configuration update. @@ -122,7 +123,7 @@ public interface ThingHandler { * * @param thing the {@link Thing}, that has been updated */ - void thingUpdated(Thing thing); + void thingUpdated(@NonNull Thing thing); /** * Notifies the handler that a channel was linked. @@ -132,7 +133,7 @@ public interface ThingHandler { * * @param channelUID UID of the linked channel */ - void channelLinked(ChannelUID channelUID); + void channelLinked(@NonNull ChannelUID channelUID); /** * Notifies the handler that a channel was unlinked. @@ -142,7 +143,7 @@ public interface ThingHandler { * * @param channelUID UID of the unlinked channel */ - void channelUnlinked(ChannelUID channelUID); + void channelUnlinked(@NonNull ChannelUID channelUID); /** * Notifies the handler that the bridge's status has changed. @@ -173,5 +174,4 @@ public interface ThingHandler { * Only then it will be removed completely. */ void handleRemoval(); - } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java index c72638997b6..81553f3c592 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.core.thing.binding; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -31,7 +32,7 @@ public interface ThingHandlerCallback { * @param channelUID channel UID (must not be null) * @param state state (must not be null) */ - void stateUpdated(ChannelUID channelUID, State state); + void stateUpdated(@NonNull ChannelUID channelUID, @NonNull State state); /** * Informs about a command, which is sent from the channel. @@ -39,7 +40,7 @@ public interface ThingHandlerCallback { * @param channelUID channel UID * @param command command */ - void postCommand(ChannelUID channelUID, Command command); + void postCommand(@NonNull ChannelUID channelUID, Command command); /** * Informs about an updated status of a thing. @@ -47,7 +48,7 @@ public interface ThingHandlerCallback { * @param thing thing (must not be null) * @param thingStatus thing status (must not be null) */ - void statusUpdated(Thing thing, ThingStatusInfo thingStatus); + void statusUpdated(@NonNull Thing thing, @NonNull ThingStatusInfo thingStatus); /** * Informs about an update of the whole thing. @@ -55,14 +56,14 @@ public interface ThingHandlerCallback { * @param thing thing that was updated (must not be null) * @throws IllegalStateException if the {@link Thing} is read-only. */ - void thingUpdated(Thing thing); + void thingUpdated(@NonNull Thing thing); /** * Informs about an updated configuration of a thing. * * @param thing thing with the updated configuration (must no be null) */ - void configurationUpdated(Thing thing); + void configurationUpdated(@NonNull Thing thing); /** * Informs the framework that the ThingType of the given {@link Thing} should be changed. @@ -71,7 +72,7 @@ public interface ThingHandlerCallback { * @param thingTypeUID the new type of the thing (must not be null) * @param configuration a configuration that should be applied to the given {@link Thing} */ - void migrateThingType(Thing thing, ThingTypeUID thingTypeUID, Configuration configuration); + void migrateThingType(@NonNull Thing thing, @NonNull ThingTypeUID thingTypeUID, Configuration configuration); /** * Informs the framework that a channel has been triggered. @@ -79,6 +80,6 @@ public interface ThingHandlerCallback { * @param channelUID UID of the channel over which has been triggered. * @param event Event. */ - void channelTriggered(Thing thing, ChannelUID channelUID, String event); + void channelTriggered(@NonNull Thing thing, @NonNull ChannelUID channelUID, String event); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerFactory.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerFactory.java index 3dd81f75e84..c2804c070bb 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerFactory.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerFactory.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.core.thing.binding; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.core.status.ConfigStatusProvider; import org.eclipse.smarthome.core.thing.Thing; @@ -29,7 +30,7 @@ public interface ThingHandlerFactory { * @param thingTypeUID the thing type UID * @return true, if the handler supports the thing type, false otherwise */ - boolean supportsThingType(ThingTypeUID thingTypeUID); + boolean supportsThingType(@NonNull ThingTypeUID thingTypeUID); /** * Creates a new {@link ThingHandler} instance. In addition, the handler can be registered as a service if it is @@ -46,7 +47,8 @@ public interface ThingHandlerFactory { * * @throws IllegalStateException if the handler instance could not be created */ - ThingHandler registerHandler(Thing thing); + @NonNull + ThingHandler registerHandler(@NonNull Thing thing); /** * Unregisters a {@link ThingHandler} instance. @@ -56,7 +58,7 @@ public interface ThingHandlerFactory { * * @param thing the thing for which the handler must be unregistered */ - void unregisterHandler(Thing thing); + void unregisterHandler(@NonNull Thing thing); /** * Creates a thing for given arguments. @@ -68,13 +70,14 @@ public interface ThingHandlerFactory { * * @return created thing */ - Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID, ThingUID bridgeUID); + Thing createThing(@NonNull ThingTypeUID thingTypeUID, @NonNull Configuration configuration, ThingUID thingUID, + ThingUID bridgeUID); /** * A thing with the given {@link Thing} UID was removed. * * @param thingUID thing UID of the removed object */ - void removeThing(ThingUID thingUID); + void removeThing(@NonNull ThingUID thingUID); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingTypeProvider.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingTypeProvider.java index a6c2f8d6d8b..33321a84871 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingTypeProvider.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingTypeProvider.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.Locale; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.type.ThingType; @@ -40,6 +41,7 @@ public interface ThingTypeProvider { * @return thing type for the given UID or null if no type for the given * UID exists */ + @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, Locale locale); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/BridgeBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/BridgeBuilder.java index e353dcabca2..868d4f37ba6 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/BridgeBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/BridgeBuilder.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Channel; @@ -29,7 +30,7 @@ */ public class BridgeBuilder extends ThingBuilder { - private BridgeBuilder(BridgeImpl thing) { + private BridgeBuilder(@NonNull BridgeImpl thing) { super(thing); } @@ -91,7 +92,7 @@ public BridgeBuilder withBridge(ThingUID bridgeUID) { } @Override - public BridgeBuilder withProperties(Map properties) { + public BridgeBuilder withProperties(Map<@NonNull String, String> properties) { return (BridgeBuilder) super.withProperties(properties); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java index 05b41ee1c43..c1da2bb3a7f 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java @@ -10,6 +10,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -31,7 +33,7 @@ public class ChannelBuilder { private ChannelKind kind; private Configuration configuration; private Set defaultTags; - private Map properties; + private Map<@NonNull String, String> properties; private String label; private String description; private ChannelTypeUID channelTypeUID; @@ -52,7 +54,7 @@ private ChannelBuilder(ChannelUID channelUID, String acceptedItemType, Set()); } @@ -62,7 +64,7 @@ public static ChannelBuilder create(ChannelUID channelUID, String acceptedItemTy * @param channelTypeUID channel type UID * @return channel builder */ - public ChannelBuilder withType(ChannelTypeUID channelTypeUID) { + public @NonNull ChannelBuilder withType(@NonNull ChannelTypeUID channelTypeUID) { this.channelTypeUID = channelTypeUID; return this; } @@ -74,7 +76,7 @@ public ChannelBuilder withType(ChannelTypeUID channelTypeUID) { * configuration * @return channel builder */ - public ChannelBuilder withConfiguration(Configuration configuration) { + public @NonNull ChannelBuilder withConfiguration(Configuration configuration) { this.configuration = configuration; return this; } @@ -85,7 +87,7 @@ public ChannelBuilder withConfiguration(Configuration configuration) { * @param properties properties to add * @return channel builder */ - public ChannelBuilder withProperties(Map properties) { + public @NonNull ChannelBuilder withProperties(Map<@NonNull String, String> properties) { this.properties = properties; return this; } @@ -96,7 +98,7 @@ public ChannelBuilder withProperties(Map properties) { * @param label the channel label to override the label set in the {@link ChannelType} * @return channel builder */ - public ChannelBuilder withLabel(String label) { + public @NonNull ChannelBuilder withLabel(String label) { this.label = label; return this; } @@ -107,7 +109,7 @@ public ChannelBuilder withLabel(String label) { * @param label the channel label to override the label set in the {@link ChannelType} * @return channel builder */ - public ChannelBuilder withDescription(String description) { + public @NonNull ChannelBuilder withDescription(String description) { this.description = description; return this; } @@ -119,7 +121,7 @@ public ChannelBuilder withDescription(String description) { * default tags * @return channel builder */ - public ChannelBuilder withDefaultTags(Set defaultTags) { + public @NonNull ChannelBuilder withDefaultTags(Set defaultTags) { this.defaultTags = defaultTags; return this; } @@ -130,7 +132,7 @@ public ChannelBuilder withDefaultTags(Set defaultTags) { * @param kind kind. * @return channel builder */ - public ChannelBuilder withKind(ChannelKind kind) { + public @NonNull ChannelBuilder withKind(ChannelKind kind) { if (kind == null) { throw new IllegalArgumentException("kind must not be null"); } @@ -144,7 +146,7 @@ public ChannelBuilder withKind(ChannelKind kind) { * * @return channel */ - public Channel build() { + public @NonNull Channel build() { return new Channel(channelUID, channelTypeUID, acceptedItemType, kind, configuration, defaultTags, properties, label, description); } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingBuilder.java index c02eddd297a..5d79f3d7627 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingBuilder.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -32,53 +33,53 @@ */ public class ThingBuilder { - private ThingImpl thing; + private @NonNull ThingImpl thing; - protected ThingBuilder(ThingImpl thing) { + protected ThingBuilder(@NonNull ThingImpl thing) { this.thing = thing; } - public static ThingBuilder create(ThingTypeUID thingTypeUID, String thingId) { + public static @NonNull ThingBuilder create(@NonNull ThingTypeUID thingTypeUID, @NonNull String thingId) { ThingImpl thing = new ThingImpl(thingTypeUID, thingId); return new ThingBuilder(thing); } @Deprecated - public static ThingBuilder create(ThingUID thingUID) { + public static @NonNull ThingBuilder create(@NonNull ThingUID thingUID) { ThingImpl thing = new ThingImpl(thingUID); return new ThingBuilder(thing); } - public static ThingBuilder create(ThingTypeUID thingTypeUID, ThingUID thingUID) { + public static @NonNull ThingBuilder create(ThingTypeUID thingTypeUID, ThingUID thingUID) { ThingImpl thing = new ThingImpl(thingTypeUID, thingUID); return new ThingBuilder(thing); } - public ThingBuilder withLabel(String label) { + public @NonNull ThingBuilder withLabel(String label) { this.thing.setLabel(label); return this; } - public ThingBuilder withChannel(Channel channel) { + public @NonNull ThingBuilder withChannel(Channel channel) { final Collection mutableThingChannels = this.thing.getChannelsMutable(); ThingHelper.ensureUniqueChannels(mutableThingChannels, channel); mutableThingChannels.add(channel); return this; } - public ThingBuilder withChannels(Channel... channels) { + public @NonNull ThingBuilder withChannels(Channel... channels) { ThingHelper.ensureUniqueChannels(channels); this.thing.setChannels(Lists.newArrayList(channels)); return this; } - public ThingBuilder withChannels(List channels) { + public @NonNull ThingBuilder withChannels(List channels) { ThingHelper.ensureUniqueChannels(channels); this.thing.setChannels(Lists.newArrayList(channels)); return this; } - public ThingBuilder withoutChannel(ChannelUID channelUID) { + public @NonNull ThingBuilder withoutChannel(ChannelUID channelUID) { Iterator iterator = this.thing.getChannelsMutable().iterator(); while (iterator.hasNext()) { if (iterator.next().getUID().equals(channelUID)) { @@ -88,19 +89,19 @@ public ThingBuilder withoutChannel(ChannelUID channelUID) { return this; } - public ThingBuilder withConfiguration(Configuration thingConfiguration) { + public @NonNull ThingBuilder withConfiguration(Configuration thingConfiguration) { this.thing.setConfiguration(thingConfiguration); return this; } - public ThingBuilder withBridge(ThingUID bridgeUID) { + public @NonNull ThingBuilder withBridge(ThingUID bridgeUID) { if (bridgeUID != null) { this.thing.setBridgeUID(bridgeUID); } return this; } - public ThingBuilder withProperties(Map properties) { + public @NonNull ThingBuilder withProperties(Map<@NonNull String, String> properties) { if (properties != null) { for (String key : properties.keySet()) { this.thing.setProperty(key, properties.get(key)); @@ -109,12 +110,12 @@ public ThingBuilder withProperties(Map properties) { return this; } - public ThingBuilder withLocation(String location) { + public @NonNull ThingBuilder withLocation(String location) { this.thing.setLocation(location); return this; } - public Thing build() { + public @NonNull Thing build() { return this.thing; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingStatusInfoBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingStatusInfoBuilder.java index 605dd685de1..905b4eb6dbe 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingStatusInfoBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ThingStatusInfoBuilder.java @@ -7,13 +7,14 @@ */ package org.eclipse.smarthome.core.thing.binding.builder; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; /** * {@link ThingStatusInfoBuilder} is responsible for creating {@link ThingStatusInfo}s. - * + * * @author Stefan Bußweiler - Initial contribution * @author Dennis Nobel - Added null checks */ @@ -33,59 +34,51 @@ private ThingStatusInfoBuilder(ThingStatus status, ThingStatusDetail statusDetai /** * Creates a status info builder for the given status and detail. - * + * * @param status the status (must not be null) * @param statusDetail the detail of the status (must not be null) * @return status info builder - * + * * @throws IllegalArgumentException if thing status or thing status detail is null */ - public static ThingStatusInfoBuilder create(ThingStatus status, ThingStatusDetail statusDetail) - throws IllegalArgumentException { - if (status == null) { - throw new IllegalArgumentException("Thing status must not be null"); - } - if (statusDetail == null) { - throw new IllegalArgumentException("Thing status detail must not be null"); - } + public static @NonNull ThingStatusInfoBuilder create(@NonNull ThingStatus status, + @NonNull ThingStatusDetail statusDetail) throws IllegalArgumentException { return new ThingStatusInfoBuilder(status, statusDetail, null); } /** * Creates a status info builder for the given status. - * + * * @param status the status (must not be null) * @return status info builder - * + * * @throws IllegalArgumentException if thing status is null */ - public static ThingStatusInfoBuilder create(ThingStatus status) throws IllegalArgumentException { + public static @NonNull ThingStatusInfoBuilder create(@NonNull ThingStatus status) throws IllegalArgumentException { return create(status, ThingStatusDetail.NONE); } /** * Appends a description to the status to build. - * + * * @param description the description * @return status info builder */ - public ThingStatusInfoBuilder withDescription(String description) throws IllegalArgumentException { + public @NonNull ThingStatusInfoBuilder withDescription(String description) throws IllegalArgumentException { this.description = description; return this; } /** * Appends a status detail to the status to build. - * + * * @param statusDetail the status detail (must not be null) * @return status info builder - * + * * @throws IllegalArgumentException if thing status detail is null */ - public ThingStatusInfoBuilder withStatusDetail(ThingStatusDetail statusDetail) throws IllegalArgumentException { - if (statusDetail == null) { - throw new IllegalArgumentException("Thing status detail must not be null"); - } + public @NonNull ThingStatusInfoBuilder withStatusDetail(@NonNull ThingStatusDetail statusDetail) + throws IllegalArgumentException { this.statusDetail = statusDetail; return this; } @@ -95,7 +88,7 @@ public ThingStatusInfoBuilder withStatusDetail(ThingStatusDetail statusDetail) t * * @return status info */ - public ThingStatusInfo build() { + public @NonNull ThingStatusInfo build() { return new ThingStatusInfo(status, statusDetail, description); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/firmware/Firmware.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/firmware/Firmware.java index 123c72703fa..2d16e38dfcb 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/firmware/Firmware.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/firmware/Firmware.java @@ -34,17 +34,17 @@ * firmware relates always to exactly one {@link ThingType}. By its {@link FirmwareUID} it is ensured that there is only * one firmware in a specific version for a thing type available. Firmwares can be easily created by the * {@link Firmware.Builder}. - * + * *

* Firmwares are made available to the system by {@link FirmwareProvider}s that are tracked by the * {@link FirmwareRegistry}. The registry can be used to get a dedicated firmware or to get all available firmwares for * a specific {@link ThingType}. - * + * *

* The {@link FirmwareUpdateService} is responsible to provide the current {@link FirmwareStatusInfo} of a thing. * Furthermore this service is the central instance to start a firmware update process. In order that the firmware of a * thing can be updated the hander of the thing has to implement the {@link FirmwareUpdateHandler} interface. - * + * *

* The {@link Firmware} implements the {@link Comparable} interface in order to be able to sort firmwares based on their * versions. Firmwares are sorted in a descending sequence, i.e. that the latest firmware will be the first @@ -54,7 +54,7 @@ * 1-9_9.9_abc. Consequently 2.0-0, 2-0_0 and 2_0.0 represent the same firmware version. * Furthermore firmware version xyz_1 is newer than firmware version abc.2 which again is newer than * firmware version 2-0-1. - * + * *

* A {@link Firmware} consists of various meta information like a version, a vendor or a description. Additionally * {@link FirmwareProvider}s can specify further meta information in form of properties (e.g. a factory reset of the @@ -215,7 +215,7 @@ public synchronized byte[] getBytes() { try (DigestInputStream dis = new DigestInputStream(inputStream, md)) { bytes = IOUtils.toByteArray(dis); } catch (IOException ioEx) { - logger.error(String.format("Cannot read firmware with UID %s.", uid), ioEx); + logger.error("Cannot read firmware with UID {}.", uid, ioEx); return null; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/dto/ThingDTOMapper.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/dto/ThingDTOMapper.java index 1a5abca5ee5..fc0d432f45b 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/dto/ThingDTOMapper.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/dto/ThingDTOMapper.java @@ -43,6 +43,7 @@ public static ThingDTO map(Thing thing) { String thingTypeUID = thing.getThingTypeUID().getAsString(); String thingUID = thing.getUID().toString(); + @SuppressWarnings("null") // thing.getBridgeUID() is checked against null before use String bridgeUID = thing.getBridgeUID() != null ? thing.getBridgeUID().toString() : null; return new ThingDTO(thingTypeUID, thingUID, thing.getLabel(), bridgeUID, channelDTOs, diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/events/ChannelTriggeredEvent.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/events/ChannelTriggeredEvent.java index c1609e42ef5..cc88ed40a07 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/events/ChannelTriggeredEvent.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/events/ChannelTriggeredEvent.java @@ -9,11 +9,12 @@ import org.eclipse.smarthome.core.events.AbstractEvent; import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.types.Type; /** * {@link ChannelTriggeredEvent}s can be used to deliver triggers through the Eclipse SmartHome event bus. * Trigger events must be created with the {@link ThingEventFactory}. + * + * @author Moritz Kammerer - Initial contribution */ public class ChannelTriggeredEvent extends AbstractEvent { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareRegistry.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareRegistry.java index ce5ccf7463f..825a171af41 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareRegistry.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareRegistry.java @@ -80,9 +80,9 @@ public Firmware getFirmware(FirmwareUID firmwareUID, Locale locale) { return firmware; } } catch (Exception e) { - logger.warn(String.format( - "Unexpected exception occurred for firmware provider %s while getting firmware for firmware UID %s.", - firmwareProvider.getClass().getSimpleName(), firmwareUID), e); + logger.warn( + "Unexpected exception occurred for firmware provider {} while getting firmware for firmware UID {}.", + firmwareProvider.getClass().getSimpleName(), firmwareUID, e); } } @@ -156,9 +156,9 @@ public Collection getFirmwares(ThingTypeUID thingTypeUID, Locale local firmwares.addAll(result); } } catch (Exception e) { - logger.warn(String.format( - "Unexpected exception occurred for firmware provider %s while getting firmwares for thing type UID %s.", - firmwareProvider.getClass().getSimpleName(), thingTypeUID), e); + logger.warn( + "Unexpected exception occurred for firmware provider {} while getting firmwares for thing type UID {}.", + firmwareProvider.getClass().getSimpleName(), thingTypeUID, e); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateService.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateService.java index 6b5ead03ad6..441a0516580 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateService.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/firmware/FirmwareUpdateService.java @@ -7,10 +7,7 @@ */ package org.eclipse.smarthome.core.thing.firmware; -import static org.eclipse.smarthome.core.thing.firmware.FirmwareStatusInfo.createUnknownInfo; -import static org.eclipse.smarthome.core.thing.firmware.FirmwareStatusInfo.createUpToDateInfo; -import static org.eclipse.smarthome.core.thing.firmware.FirmwareStatusInfo.createUpdateAvailableInfo; -import static org.eclipse.smarthome.core.thing.firmware.FirmwareStatusInfo.createUpdateExecutableInfo; +import static org.eclipse.smarthome.core.thing.firmware.FirmwareStatusInfo.*; import java.net.URI; import java.net.URISyntaxException; @@ -34,8 +31,8 @@ import org.eclipse.smarthome.core.events.EventFilter; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.events.EventSubscriber; -import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingUID; @@ -233,14 +230,13 @@ public Void call() { } }, timeout); } catch (ExecutionException e) { - logger.error(String.format( - "Unexpected exception occurred for firmware update of thing with UID %s and firmware with UID %s.", - thingUID, firmwareUID), e.getCause()); + logger.error( + "Unexpected exception occurred for firmware update of thing with UID {} and firmware with UID {}.", + thingUID, firmwareUID, e.getCause()); progressCallback.failedInternal("unexpected-handler-error"); } catch (TimeoutException e) { - logger.error(String.format( - "Timeout occurred for firmware update of thing with UID %s and firmware with UID %s.", - thingUID, firmwareUID), e); + logger.error("Timeout occurred for firmware update of thing with UID {} and firmware with UID {}.", + thingUID, firmwareUID, e); progressCallback.failedInternal("timeout-error"); } } @@ -250,7 +246,7 @@ public Void call() { /** * Cancels the firmware update of the thing having the given thing UID by invoking the operation * {@link FirmwareUpdateHandler#cancel()} of the thing´s firmware update handler. - * + * * @param thingUID the thing UID (must not be null) */ public void cancelFirmwareUpdate(final ThingUID thingUID) { @@ -274,13 +270,11 @@ public Void call() { } }); } catch (ExecutionException e) { - logger.error(String.format( - "Unexpected exception occurred while canceling firmware update of thing with UID %s.", - thingUID), e.getCause()); + logger.error("Unexpected exception occurred while canceling firmware update of thing with UID {}.", + thingUID, e.getCause()); progressCallback.failedInternal("unexpected-handler-error-during-cancel"); } catch (TimeoutException e) { - logger.error(String.format("Timeout occurred while canceling firmware update of thing with UID %s.", - thingUID), e); + logger.error("Timeout occurred while canceling firmware update of thing with UID {}.", thingUID, e); progressCallback.failedInternal("timeout-error-during-cancel"); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nLocalizationService.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nLocalizationService.java index 0207ebd8e31..8c7dcfa1a57 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nLocalizationService.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nLocalizationService.java @@ -15,7 +15,9 @@ import org.eclipse.smarthome.core.thing.type.BridgeType; import org.eclipse.smarthome.core.thing.type.ChannelDefinition; import org.eclipse.smarthome.core.thing.type.ChannelGroupDefinition; +import org.eclipse.smarthome.core.thing.type.ChannelGroupType; import org.eclipse.smarthome.core.thing.type.ThingType; +import org.eclipse.smarthome.core.thing.type.TypeResolver; import org.osgi.framework.Bundle; /** @@ -23,6 +25,7 @@ * framework. * * @author Markus Rathgeb - Move code from XML thing type provider to separate service + * @author Laurent Garnier - fix localized label and description for channel group definition */ public class ThingTypeI18nLocalizationService { @@ -57,10 +60,17 @@ public ThingType createLocalizedThingType(Bundle bundle, ThingType thingType, Lo final List localizedChannelGroupDefinitions = new ArrayList<>( thingType.getChannelGroupDefinitions().size()); for (final ChannelGroupDefinition channelGroupDefinition : thingType.getChannelGroupDefinitions()) { - final String channelGroupLabel = this.thingTypeI18nUtil.getChannelGroupLabel(bundle, - channelGroupDefinition.getTypeUID(), channelGroupDefinition.getLabel(), locale); - final String channelGroupDescription = this.thingTypeI18nUtil.getChannelGroupDescription(bundle, - channelGroupDefinition.getTypeUID(), channelGroupDefinition.getDescription(), locale); + ChannelGroupType channelGroupType = TypeResolver.resolve(channelGroupDefinition.getTypeUID(), locale); + final String channelGroupLabel = channelGroupDefinition.getLabel() == null + ? this.thingTypeI18nUtil.getChannelGroupLabel(bundle, channelGroupType.getUID(), + channelGroupType.getLabel(), locale) + : this.thingTypeI18nUtil.getChannelGroupLabel(bundle, thingType.getUID(), + channelGroupDefinition.getId(), channelGroupDefinition.getLabel(), locale); + final String channelGroupDescription = channelGroupDefinition.getDescription() == null + ? this.thingTypeI18nUtil.getChannelGroupDescription(bundle, channelGroupType.getUID(), + channelGroupType.getDescription(), locale) + : this.thingTypeI18nUtil.getChannelGroupDescription(bundle, thingType.getUID(), + channelGroupDefinition.getId(), channelGroupDefinition.getDescription(), locale); localizedChannelGroupDefinitions.add(new ChannelGroupDefinition(channelGroupDefinition.getId(), channelGroupDefinition.getTypeUID(), channelGroupLabel, channelGroupDescription)); } @@ -68,12 +78,12 @@ public ThingType createLocalizedThingType(Bundle bundle, ThingType thingType, Lo if (thingType instanceof BridgeType) { final BridgeType bridgeType = (BridgeType) thingType; return new BridgeType(bridgeType.getUID(), bridgeType.getSupportedBridgeTypeUIDs(), label, description, - thingType.isListed(), localizedChannelDefinitions, localizedChannelGroupDefinitions, - thingType.getProperties(), bridgeType.getConfigDescriptionURI()); + thingType.isListed(), thingType.getRepresentationProperty(), localizedChannelDefinitions, + localizedChannelGroupDefinitions, thingType.getProperties(), bridgeType.getConfigDescriptionURI()); } else { return new ThingType(thingType.getUID(), thingType.getSupportedBridgeTypeUIDs(), label, description, - thingType.isListed(), localizedChannelDefinitions, localizedChannelGroupDefinitions, - thingType.getProperties(), thingType.getConfigDescriptionURI()); + thingType.isListed(), thingType.getRepresentationProperty(), localizedChannelDefinitions, + localizedChannelGroupDefinitions, thingType.getProperties(), thingType.getConfigDescriptionURI()); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nUtil.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nUtil.java index 3e1f9c8742c..54a072c5de5 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nUtil.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/i18n/ThingTypeI18nUtil.java @@ -9,8 +9,8 @@ import java.util.Locale; -import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.i18n.I18nUtil; +import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; @@ -22,6 +22,7 @@ * not a constant. * * @author Dennis Nobel - Initial contribution + * @author Laurent Garnier - add translation for channel group label and channel group description */ public class ThingTypeI18nUtil { @@ -45,16 +46,30 @@ public String getChannelGroupDescription(Bundle bundle, ChannelGroupTypeUID chan return i18nProvider.getText(bundle, key, defaultDescription, locale); } + public String getChannelGroupDescription(Bundle bundle, ThingTypeUID thingTypeUID, String channelGroupId, + String defaultDescription, Locale locale) { + String key = I18nUtil.isConstant(defaultDescription) ? I18nUtil.stripConstant(defaultDescription) + : inferThingTypeKey(thingTypeUID, channelGroupId, "description"); + return i18nProvider.getText(bundle, key, defaultDescription, locale); + } + + public String getChannelLabel(Bundle bundle, ChannelTypeUID channelTypeUID, String defaultLabel, Locale locale) { + String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) + : inferChannelKey(channelTypeUID, "label"); + return i18nProvider.getText(bundle, key, defaultLabel, locale); + } + public String getChannelGroupLabel(Bundle bundle, ChannelGroupTypeUID channelGroupTypeUID, String defaultLabel, Locale locale) { - String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) : inferChannelKey( - channelGroupTypeUID, "label"); + String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) + : inferChannelKey(channelGroupTypeUID, "label"); return i18nProvider.getText(bundle, key, defaultLabel, locale); } - public String getChannelLabel(Bundle bundle, ChannelTypeUID channelTypeUID, String defaultLabel, Locale locale) { - String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) : inferChannelKey( - channelTypeUID, "label"); + public String getChannelGroupLabel(Bundle bundle, ThingTypeUID thingTypeUID, String channelGroupId, + String defaultLabel, Locale locale) { + String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) + : inferThingTypeKey(thingTypeUID, channelGroupId, "label"); return i18nProvider.getText(bundle, key, defaultLabel, locale); } @@ -67,8 +82,8 @@ public String getChannelStateOption(Bundle bundle, ChannelTypeUID channelTypeUID public String getChannelStatePattern(Bundle bundle, ChannelTypeUID channelTypeUID, String defaultPattern, Locale locale) { - String key = I18nUtil.isConstant(defaultPattern) ? I18nUtil.stripConstant(defaultPattern) : inferChannelKey( - channelTypeUID, "state.pattern"); + String key = I18nUtil.isConstant(defaultPattern) ? I18nUtil.stripConstant(defaultPattern) + : inferChannelKey(channelTypeUID, "state.pattern"); return i18nProvider.getText(bundle, key, defaultPattern, locale); } @@ -79,8 +94,8 @@ public String getDescription(Bundle bundle, ThingTypeUID thingTypeUID, String de } public String getLabel(Bundle bundle, ThingTypeUID thingTypeUID, String defaultLabel, Locale locale) { - String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) : inferThingTypeKey( - thingTypeUID, "label"); + String key = I18nUtil.isConstant(defaultLabel) ? I18nUtil.stripConstant(defaultLabel) + : inferThingTypeKey(thingTypeUID, "label"); return i18nProvider.getText(bundle, key, defaultLabel, locale); } @@ -97,4 +112,9 @@ private String inferThingTypeKey(ThingTypeUID thingTypeUID, String lastSegment) return "thing-type." + thingTypeUID.getBindingId() + "." + thingTypeUID.getId() + "." + lastSegment; } + private String inferThingTypeKey(ThingTypeUID thingTypeUID, String channelGroupId, String lastSegment) { + return "thing-type." + thingTypeUID.getBindingId() + "." + thingTypeUID.getId() + ".group." + channelGroupId + + "." + lastSegment; + } + } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/Activator.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/Activator.java index 429a9e05eb6..c19db1f432f 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/Activator.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/Activator.java @@ -15,6 +15,10 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +/** + * + * @author Denis Nobel - Initial contribution + */ public class Activator implements BundleActivator { public static class ChannelTypeRegistryBinder { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/BridgeImpl.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/BridgeImpl.java index b721b9d6a5b..b730ec1aa76 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/BridgeImpl.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/BridgeImpl.java @@ -21,6 +21,10 @@ import com.google.common.collect.ImmutableList; +/** + * + * @author Denis Nobel - Initial contribution + */ public class BridgeImpl extends ThingImpl implements Bridge { private transient List things = new CopyOnWriteArrayList<>(); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java index 23aeaafdb49..7b0a1da80d0 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java @@ -10,6 +10,7 @@ import java.math.BigDecimal; import java.net.URI; import java.util.List; + import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; @@ -28,6 +29,7 @@ import org.eclipse.smarthome.core.thing.type.TypeResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.common.collect.Lists; /** @@ -41,7 +43,7 @@ */ public class ThingFactoryHelper { - private static Logger logger = LoggerFactory.getLogger(ThingFactory.class); + private static Logger logger = LoggerFactory.getLogger(ThingFactoryHelper.class); /** * Create {@link Channel} instances for the given Thing. @@ -157,8 +159,8 @@ public static Object getDefaultValueAsCorrectType(Type parameterType, String def return null; } } catch (NumberFormatException ex) { - LoggerFactory.getLogger(ThingFactory.class).warn("Could not parse default value '" + defaultValue - + "' as type '" + parameterType + "': " + ex.getMessage(), ex); + LoggerFactory.getLogger(ThingFactory.class).warn("Could not parse default value '{}' as type '{}': {}", + defaultValue, parameterType, ex.getMessage(), ex); return null; } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java index d6a7637ffb5..a8af488a6fd 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java @@ -307,8 +307,13 @@ public void run() { // Register the new Handler - ThingManager.updateThing() is going to take care of that thingRegistry.update(thing); + ThingHandler handler = thing.getHandler(); + String handlerString = "NO HANDLER"; + if (handler != null) { + handlerString = handler.toString(); + } logger.debug("Changed ThingType of Thing {} to {}. New ThingHandler is {}.", - thing.getUID().toString(), thing.getThingTypeUID(), thing.getHandler().toString()); + thing.getUID().toString(), thing.getThingTypeUID(), handlerString); } finally { lock.unlock(); } @@ -385,9 +390,8 @@ public Void call() throws Exception { logger.error("Exception occurred while calling handler: {}", ex.getMessage(), ex); } } else { - logger.debug( - "Not delegating command '{}' for item '{}' to handler for channel '{}', " - + "because handler is not initialized (thing must be in status UNKNOWN, ONLINE or OFFLINE).", + logger.debug("Not delegating command '{}' for item '{}' to handler for channel '{}', " + + "because handler is not initialized (thing must be in status UNKNOWN, ONLINE or OFFLINE).", command, itemName, channelUID); } } else { @@ -424,7 +428,12 @@ protected void receiveUpdate(ItemStateEvent updateEvent) { SafeMethodCaller.call(new SafeMethodCaller.ActionWithException() { @Override public Void call() throws Exception { - handler.handleUpdate(channelUID, newState); + if (newState != null) { + handler.handleUpdate(channelUID, newState); + } else { + throw new IllegalStateException( + "Trying to set state to null on channel " + channelUID); + } return null; } }); @@ -435,9 +444,8 @@ public Void call() throws Exception { logger.error("Exception occurred while calling handler: {}", ex.getMessage(), ex); } } else { - logger.debug( - "Not delegating update '{}' for item '{}' to handler for channel '{}', " - + "because handler is not initialized (thing must be in status UNKNOWN, ONLINE or OFFLINE).", + logger.debug("Not delegating update '{}' for item '{}' to handler for channel '{}', " + + "because handler is not initialized (thing must be in status UNKNOWN, ONLINE or OFFLINE).", newState, itemName, channelUID); } } else { @@ -544,7 +552,7 @@ public Void call() throws Exception { registerAndInitializeHandler(thing, getThingHandlerFactory(thing)); } - if (oldThing != thing) { + if (oldThing != thing && oldThing != null) { oldThing.setHandler(null); } } finally { @@ -653,9 +661,14 @@ private void initializeHandler(Thing thing) { thing.getUID()); return; } - if (thing.getHandler().getThing() != thing) { - logger.debug("The model of {} is inconsistent [thing.getHandler().getThing() != thing]", - thing.getUID()); + ThingHandler handler = thing.getHandler(); + if (handler == null) { + throw new IllegalStateException("Handler should not be null here"); + } else { + if (handler.getThing() != thing) { + logger.debug("The model of {} is inconsistent [thing.getHandler().getThing() != thing]", + thing.getUID()); + } } ThingType thingType = getThingType(thing); applyDefaultConfiguration(thing, thingType); @@ -708,7 +721,8 @@ private ConfigDescription resolve(URI configDescriptionURI, Locale locale) { } return configDescriptionRegistry != null - ? configDescriptionRegistry.getConfigDescription(configDescriptionURI, locale) : null; + ? configDescriptionRegistry.getConfigDescription(configDescriptionURI, locale) + : null; } private List getRequiredParameters(ConfigDescription description) { @@ -787,7 +801,9 @@ private void doUnregisterHandler(final Thing thing, final ThingHandlerFactory th public Void call() throws Exception { ThingHandler thingHandler = thing.getHandler(); thingHandlerFactory.unregisterHandler(thing); - thingHandler.setCallback(null); + if (thingHandler != null) { + thingHandler.setCallback(null); + } thing.setHandler(null); setThingStatus(thing, buildStatusInfo(ThingStatus.UNINITIALIZED, ThingStatusDetail.HANDLER_MISSING_ERROR)); @@ -899,7 +915,10 @@ public void run() { try { BridgeHandler bridgeHandler = bridge.getHandler(); if (bridgeHandler != null) { - bridgeHandler.childHandlerInitialized(thing.getHandler(), thing); + ThingHandler thingHandler = thing.getHandler(); + if (thingHandler != null) { + bridgeHandler.childHandlerInitialized(thingHandler, thing); + } } } catch (Exception e) { logger.error( @@ -1135,7 +1154,7 @@ private void setThingStatus(Thing thing, ThingStatusInfo thingStatusInfo) { ThingEventFactory.createStatusInfoChangedEvent(thing.getUID(), newStatusInfo, oldStatusInfo)); } } catch (Exception ex) { - logger.error("Could not post 'ThingStatusInfoEvent' event: " + ex.getMessage(), ex); + logger.error("Could not post 'ThingStatusInfoEvent' event: {}", ex.getMessage(), ex); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingRegistryImpl.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingRegistryImpl.java index b58a9ab04ff..99d19f5237a 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingRegistryImpl.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingRegistryImpl.java @@ -13,6 +13,7 @@ import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.common.registry.AbstractRegistry; import org.eclipse.smarthome.core.common.registry.Provider; @@ -62,18 +63,6 @@ public void addThingTracker(ThingTracker thingTracker) { thingTrackers.add(thingTracker); } - @Override - public Thing get(final ThingUID uid) { - for (final Map.Entry, Collection> entry : elementMap.entrySet()) { - for (final Thing thing : entry.getValue()) { - if (uid.equals(thing.getUID())) { - return thing; - } - } - } - return null; - } - @Override public Channel getChannel(ChannelUID channelUID) { ThingUID thingUID = channelUID.getThingUID(); @@ -85,7 +74,7 @@ public Channel getChannel(ChannelUID channelUID) { } @Override - public void updateConfiguration(ThingUID thingUID, Map configurationParameters) { + public void updateConfiguration(ThingUID thingUID, Map<@NonNull String, Object> configurationParameters) { Thing thing = get(thingUID); if (thing != null) { ThingHandler thingHandler = thing.getHandler(); @@ -224,8 +213,8 @@ private void notifyTrackers(Thing thing, ThingTrackerEvent event) { break; } } catch (Exception ex) { - logger.error("Could not inform the ThingTracker '" + thingTracker + "' about the '" + event.name() - + "' event!", ex); + logger.error("Could not inform the ThingTracker '{}' about the '{}' event!", thingTracker, event.name(), + ex); } } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/AbstractLinkRegistry.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/AbstractLinkRegistry.java index 29a9ceeb7e2..73663fbacd1 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/AbstractLinkRegistry.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/AbstractLinkRegistry.java @@ -7,9 +7,7 @@ */ package org.eclipse.smarthome.core.thing.link; -import java.util.Collection; import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; import org.eclipse.smarthome.core.common.registry.AbstractRegistry; @@ -85,16 +83,4 @@ public Set getLinks(UID uid) { return links; } - @Override - public L get(final String key) { - for (final Map.Entry, Collection> entry : elementMap.entrySet()) { - for (final L link : entry.getValue()) { - if (key.equals(link.getUID())) { - return link; - } - } - } - return null; - } - } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemChannelLinkProvider.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemChannelLinkProvider.java index 9ba6b42e1fc..555d8716346 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemChannelLinkProvider.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemChannelLinkProvider.java @@ -19,8 +19,8 @@ * @author Dennis Nobel - Initial contribution * */ -public class ManagedItemChannelLinkProvider extends DefaultAbstractManagedProvider implements - ItemChannelLinkProvider { +public class ManagedItemChannelLinkProvider extends DefaultAbstractManagedProvider + implements ItemChannelLinkProvider { @Override protected String getStorageName() { @@ -32,11 +32,6 @@ protected String keyToString(String key) { return key; } - @Override - protected String getKey(ItemChannelLink element) { - return element.getUID(); - } - public void removeLinksForThing(ThingUID thingUID) { Collection itemChannelLinks = getAll(); for (ItemChannelLink itemChannelLink : itemChannelLinks) { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemThingLinkProvider.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemThingLinkProvider.java index 43e274130df..cfb920e3b64 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemThingLinkProvider.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ManagedItemThingLinkProvider.java @@ -16,13 +16,8 @@ * @author Dennis Nobel - Initial contribution * */ -public class ManagedItemThingLinkProvider extends DefaultAbstractManagedProvider implements - ItemThingLinkProvider { - - @Override - protected String getKey(ItemThingLink element) { - return element.getUID(); - } +public class ManagedItemThingLinkProvider extends DefaultAbstractManagedProvider + implements ItemThingLinkProvider { @Override protected String getStorageName() { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ThingLinkManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ThingLinkManager.java index 06308e6422d..078dc4f8428 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ThingLinkManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/link/ThingLinkManager.java @@ -263,7 +263,7 @@ private void informHandlerAboutLinkedChannel(Thing thing, Channel channel) { try { handler.channelLinked(channel.getUID()); } catch (Exception ex) { - logger.error("Exception occurred while informing handler:" + ex.getMessage(), ex); + logger.error("Exception occurred while informing handler: {}", ex.getMessage(), ex); } } else { logger.trace("Can not inform handler about linked channel, because no handler is assigned to the thing {}.", @@ -282,7 +282,7 @@ private void informHandlerAboutUnlinkedChannel(Thing thing, Channel channel) { try { handler.channelUnlinked(channel.getUID()); } catch (Exception ex) { - logger.error("Exception occurred while informing handler:" + ex.getMessage(), ex); + logger.error("Exception occurred while informing handler: {}", ex.getMessage(), ex); } } else { logger.trace( diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/BridgeType.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/BridgeType.java index f9ccacbd801..f87a4f12ab5 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/BridgeType.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/BridgeType.java @@ -24,6 +24,7 @@ * * @author Michael Grammling - Initial Contribution * @author Thomas Höfer - Added thing and thing type properties + * @author Andre Fuechsel - Added representationProperty */ public class BridgeType extends ThingType { @@ -32,7 +33,7 @@ public class BridgeType extends ThingType { */ public BridgeType(String bindingId, String thingTypeId, String label) throws IllegalArgumentException { - this(new ThingTypeUID(bindingId, thingTypeId), null, label, null, true, null, null, null, null); + this(new ThingTypeUID(bindingId, thingTypeId), null, label, null, true, null, null, null, null, null); } /** @@ -66,7 +67,7 @@ public BridgeType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String List channelDefinitions, List channelGroupDefinitions, Map properties, URI configDescriptionURI) throws IllegalArgumentException { - this(uid, supportedBridgeTypeUIDs, label, description, true, channelDefinitions, channelGroupDefinitions, + this(uid, supportedBridgeTypeUIDs, label, description, true, null, channelDefinitions, channelGroupDefinitions, properties, configDescriptionURI); } @@ -85,13 +86,13 @@ public BridgeType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String * @param description the human readable description for the according type * (could be null or empty) * - * @param listed detemines whether it should be displayed for manually pairing or not + * @param listed determines whether it should be displayed for manually pairing or not * * @param channelDefinitions the channels this Bridge type provides (could be null or empty) * * @param channelGroupDefinitions the channel groups defining the channels this Bridge * type provides (could be null or empty) - * + * * @param properties the properties this Bridge type provides (could be null) * * @param configDescriptionURI the link to the concrete ConfigDescription (could be null) @@ -104,8 +105,48 @@ public BridgeType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String List channelGroupDefinitions, Map properties, URI configDescriptionURI) throws IllegalArgumentException { - super(uid, supportedBridgeTypeUIDs, label, description, listed, channelDefinitions, channelGroupDefinitions, - properties, configDescriptionURI); + this(uid, supportedBridgeTypeUIDs, label, description, listed, null, channelDefinitions, + channelGroupDefinitions, properties, configDescriptionURI); + } + + /** + * Creates a new instance of this class with the specified parameters. + * + * @param uid the unique identifier which identifies this Bridge type within + * the overall system (must neither be null, nor empty) + * + * @param supportedBridgeTypeUIDs the unique identifiers to the bridges this Bridge type + * supports (could be null or empty) + * + * @param label the human readable label for the according type + * (must neither be null nor empty) + * + * @param description the human readable description for the according type + * (could be null or empty) + * + * @param listed determines whether it should be displayed for manually pairing or not + * + * @param representationProperty name of the property that uniquely identifies this Thing + * + * @param channelDefinitions the channels this Bridge type provides (could be null or empty) + * + * @param channelGroupDefinitions the channel groups defining the channels this Bridge + * type provides (could be null or empty) + * + * @param properties the properties this Bridge type provides (could be null) + * + * @param configDescriptionURI the link to the concrete ConfigDescription (could be null) + * + * @throws IllegalArgumentException if the UID is null or empty, + * or the the meta information is null + */ + public BridgeType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String label, String description, + boolean listed, String representationProperty, List channelDefinitions, + List channelGroupDefinitions, Map properties, + URI configDescriptionURI) throws IllegalArgumentException { + + super(uid, supportedBridgeTypeUIDs, label, description, listed, representationProperty, channelDefinitions, + channelGroupDefinitions, properties, configDescriptionURI); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelGroupType.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelGroupType.java index 6a1fa78cae8..6b69680b1e6 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelGroupType.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelGroupType.java @@ -24,8 +24,11 @@ public class ChannelGroupType extends AbstractDescriptionType { private final boolean advanced; private final List channelDefinitions; + private final String category; /** + * Deprecated: Will be removed before 1.0 release in favor for constructor with category + * * Creates a new instance of this class with the specified parameters. * * @param uid the unique identifier which identifies this channel group type within the @@ -44,12 +47,50 @@ public class ChannelGroupType extends AbstractDescriptionType { * * @throws IllegalArgumentException if the UID is null, or the label is null or empty */ + @Deprecated public ChannelGroupType(ChannelGroupTypeUID uid, boolean advanced, String label, String description, List channelDefinitions) throws IllegalArgumentException { super(uid, label, description); this.advanced = advanced; + this.category = null; + + if (channelDefinitions != null) { + this.channelDefinitions = Collections.unmodifiableList(channelDefinitions); + } else { + this.channelDefinitions = Collections.unmodifiableList(new ArrayList(0)); + } + } + + /** + * Creates a new instance of this class with the specified parameters. + * + * @param uid the unique identifier which identifies this channel group type within the + * overall system (must neither be null, nor empty) + * + * @param advanced true if this channel group type contains advanced features, otherwise false + * + * @param label the human readable label for the according type + * (must neither be null nor empty) + * + * @param description the human readable description for the according type + * (could be null or empty) + * + * @param category the category of this channel group type, e.g. Temperature (could be null or empty) + * + * @param channelDefinitions the channel definitions this channel group forms + * (could be null or empty) + * + * @throws IllegalArgumentException if the UID is null, or the label is null or empty + */ + public ChannelGroupType(ChannelGroupTypeUID uid, boolean advanced, String label, String description, + String category, List channelDefinitions) throws IllegalArgumentException { + + super(uid, label, description); + + this.advanced = advanced; + this.category = category; if (channelDefinitions != null) { this.channelDefinitions = Collections.unmodifiableList(channelDefinitions); @@ -62,7 +103,7 @@ public ChannelGroupType(ChannelGroupTypeUID uid, boolean advanced, String label, * Returns {@code true} if this {@link ChannelGroupType} contains advanced functionalities * which should be typically not shown in the basic view of user interfaces, * otherwise {@code false}. - * + * * @return true if this channel group contains advanced functionalities, otherwise false */ public boolean isAdvanced() { @@ -80,6 +121,10 @@ public List getChannelDefinitions() { return channelDefinitions; } + public String getCategory() { + return this.category; + } + @Override public ChannelGroupTypeUID getUID() { return (ChannelGroupTypeUID) super.getUID(); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ThingType.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ThingType.java index a9fe52fb8d4..68ade675328 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ThingType.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ThingType.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -30,21 +32,23 @@ * @author Dennis Nobel - Initial Contribution * @author Thomas Höfer - Added thing and thing type properties * @author Simon Kaufmann - Added listed field + * @author Andre Fuechsel - Added representationProperty field */ public class ThingType extends AbstractDescriptionType { private final List channelGroupDefinitions; private final List channelDefinitions; private final List supportedBridgeTypeUIDs; - private final Map properties; - private URI configDescriptionURI; - private boolean listed; + private final Map<@NonNull String, String> properties; + private final String representationProperty; + private final URI configDescriptionURI; + private final boolean listed; /** * @see ThingType#ThingType(ThingTypeUID, List, String, String, List, List, Map, URI) */ public ThingType(String bindingId, String thingTypeId, String label) throws IllegalArgumentException { - this(new ThingTypeUID(bindingId, thingTypeId), null, label, null, true, null, null, null, null); + this(new ThingTypeUID(bindingId, thingTypeId), null, label, null, true, null, null, null, null, null); } /** @@ -77,7 +81,7 @@ public ThingType(String bindingId, String thingTypeId, String label) throws Ille public ThingType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String label, String description, List channelDefinitions, List channelGroupDefinitions, Map properties, URI configDescriptionURI) throws IllegalArgumentException { - this(uid, supportedBridgeTypeUIDs, label, description, true, channelDefinitions, channelGroupDefinitions, + this(uid, supportedBridgeTypeUIDs, label, description, true, null, channelDefinitions, channelGroupDefinitions, properties, configDescriptionURI); } @@ -94,9 +98,9 @@ public ThingType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String * (must neither be null nor empty) * * @param description the human readable description for the according type - * (could be null or empty)6 + * (could be null or empty) * - * @param listed detemines whether it should be listed for manually pairing or not + * @param listed determines whether it should be listed for manually pairing or not * * @param channelDefinitions the channels this Thing type provides (could be null or empty) * @@ -114,11 +118,52 @@ public ThingType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String boolean listed, List channelDefinitions, List channelGroupDefinitions, Map properties, URI configDescriptionURI) throws IllegalArgumentException { + this(uid, supportedBridgeTypeUIDs, label, description, listed, null, channelDefinitions, + channelGroupDefinitions, properties, configDescriptionURI); + } + + /** + * Creates a new instance of this class with the specified parameters. + * + * @param uid the unique identifier which identifies this Thing type within the overall system + * (must neither be null, nor empty) + * + * @param supportedBridgeTypeUIDs the unique identifiers of the bridges this Thing type supports + * (could be null or empty) + * + * @param label the human readable label for the according type + * (must neither be null nor empty) + * + * @param description the human readable description for the according type + * (could be null or empty) + * + * @param listed determines whether it should be listed for manually pairing or not + * + * @param representationProperty name of the property that uniquely identifies this Thing + * + * @param channelDefinitions the channels this Thing type provides (could be null or empty) + * + * @param channelGroupDefinitions the channel groups defining the channels this Thing type + * provides (could be null or empty) + * + * @param properties the properties this Thing type provides (could be null) + * + * @param configDescriptionURI the link to the concrete ConfigDescription (could be null) + * + * @throws IllegalArgumentException + * if the UID is null or empty, or the the meta information is null + */ + public ThingType(ThingTypeUID uid, List supportedBridgeTypeUIDs, String label, String description, + boolean listed, String representationProperty, List channelDefinitions, + List channelGroupDefinitions, Map properties, + URI configDescriptionURI) throws IllegalArgumentException { super(uid, label, description); this.listed = listed; + this.representationProperty = representationProperty; + if (supportedBridgeTypeUIDs != null) { this.supportedBridgeTypeUIDs = Collections.unmodifiableList(supportedBridgeTypeUIDs); } else { @@ -215,7 +260,7 @@ public URI getConfigDescriptionURI() { * * @return the properties for this {@link ThingType} (not null) */ - public Map getProperties() { + public @NonNull Map<@NonNull String, String> getProperties() { return properties; } @@ -251,10 +296,24 @@ public ChannelTypeUID getChannelTypeUID(ChannelUID channelUID) { return null; } + /** + * Check, if things of this thing type should be listed for manually pairing or not. + * + * @return {@code true}, if manual pairing is allowed + */ public boolean isListed() { return listed; } + /** + * Get the name of the representation property of this thing type. May be {code null}. + * + * @return representation property name or {@code null} + */ + public @Nullable String getRepresentationProperty() { + return representationProperty; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/TransformationHelper.java b/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/TransformationHelper.java index e3fc125147e..d2119dc174e 100644 --- a/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/TransformationHelper.java +++ b/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/TransformationHelper.java @@ -17,6 +17,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class TransformationHelper { private final static Logger logger = LoggerFactory.getLogger(TransformationHelper.class); @@ -26,19 +30,18 @@ public class TransformationHelper { /** * determines whether a pattern refers to a transformation service - * + * * @param pattern the pattern to check * @return true, if the pattern contains a transformation */ static public boolean isTransform(String pattern) { - return EXTRACT_TRANSFORMFUNCTION_PATTERN.matcher(pattern).matches(); + return EXTRACT_TRANSFORMFUNCTION_PATTERN.matcher(pattern).matches(); } - /** * Queries the OSGi service registry for a service that provides a transformation service of * a given transformation type (e.g. REGEX, XSLT, etc.) - * + * * @param transformationType the desired transformation type * @return a service instance or null, if none could be found */ @@ -47,15 +50,17 @@ static public TransformationService getTransformationService(BundleContext conte Logger logger = LoggerFactory.getLogger(TransformationHelper.class); String filter = "(smarthome.transform=" + transformationType + ")"; try { - Collection> refs = context.getServiceReferences( - TransformationService.class, filter); + Collection> refs = context + .getServiceReferences(TransformationService.class, filter); if (refs != null && refs.size() > 0) { return context.getService(refs.iterator().next()); } else { - logger.warn("Cannot get service reference for transformation service of type " + transformationType); + logger.warn("Cannot get service reference for transformation service of type {}", + transformationType); } } catch (InvalidSyntaxException e) { - logger.warn("Cannot get service reference for transformation service of type " + transformationType, e); + logger.warn("Cannot get service reference for transformation service of type {}", transformationType, + e); } } return null; @@ -63,7 +68,7 @@ static public TransformationService getTransformationService(BundleContext conte /** * Transforms a state string using transformation functions within a given pattern. - * + * * @param context a valid bundle context, required for accessing the services * @param stateDescPattern the pattern that contains the transformation instructions * @param state the state to be formatted before being passed into the transformation function @@ -77,23 +82,22 @@ public static String transform(BundleContext context, String stateDescPattern, S String value = matcher.group(3); TransformationService transformation = TransformationHelper.getTransformationService(context, type); if (transformation != null) { - value = String.format(value, state); + value = String.format(value, state); try { pattern = transformation.transform(pattern, value); } catch (TransformationException e) { - logger.warn("transformation throws exception [transformation=" + transformation + ", value=" - + value + "]", e); + logger.warn("transformation throws exception [transformation={}, value={}]", transformation, value, + e); pattern = state; } } else { - logger.warn( - "couldn't transform value because transformationService of type '{}' is unavailable", + logger.warn("couldn't transform value because transformationService of type '{}' is unavailable", type); pattern = state; } return pattern; } else { - return state; + return state; } } diff --git a/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/actions/Transformation.java b/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/actions/Transformation.java index 31815cb268d..66ba190c71b 100644 --- a/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/actions/Transformation.java +++ b/bundles/core/org.eclipse.smarthome.core.transform/src/main/java/org/eclipse/smarthome/core/transform/actions/Transformation.java @@ -25,7 +25,7 @@ public class Transformation { /** * Applies a transformation of a given type with some function to a value. - * + * * @param type the transformation type, e.g. REGEX or MAP * @param function the function to call, this value depends on the transformation type * @param value the value to apply the transformation to @@ -35,18 +35,18 @@ public class Transformation { */ public static String transform(String type, String function, String value) { String result; - TransformationService service = TransformationHelper.getTransformationService( - TransformationActivator.getContext(), type); + TransformationService service = TransformationHelper + .getTransformationService(TransformationActivator.getContext(), type); Logger logger = LoggerFactory.getLogger(Transformation.class); if (service != null) { try { result = service.transform(function, value); } catch (TransformationException e) { - logger.error("Error executing the transformation '" + type + "': " + e.getMessage()); + logger.error("Error executing the transformation '{}': {}", type, e.getMessage()); result = value; } } else { - logger.warn("No transformation service '" + type + "' could be found."); + logger.warn("No transformation service '{}' could be found.", type); result = value; } return result; diff --git a/bundles/core/org.eclipse.smarthome.core.voice/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.voice/META-INF/MANIFEST.MF index 6149032ece8..2c3b39e9479 100644 --- a/bundles/core/org.eclipse.smarthome.core.voice/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.voice/META-INF/MANIFEST.MF @@ -18,6 +18,8 @@ Import-Package: org.apache.commons.collections.map, org.eclipse.smarthome.core.items.events, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.types, + org.eclipse.smarthome.core.voice, + org.eclipse.smarthome.core.voice.text, org.eclipse.smarthome.io.console, org.eclipse.smarthome.io.console.extensions, org.osgi.framework, diff --git a/bundles/core/org.eclipse.smarthome.core/ESH-INF/config/networkinterface.xml b/bundles/core/org.eclipse.smarthome.core/ESH-INF/config/networkinterface.xml new file mode 100644 index 00000000000..f82fe5c61bd --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core/ESH-INF/config/networkinterface.xml @@ -0,0 +1,15 @@ + + + + + + + A subnet (e.g. 192.168.1.0/24) + true + + + + diff --git a/bundles/core/org.eclipse.smarthome.core/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core/META-INF/MANIFEST.MF index 472baeaea7b..1a088d990aa 100644 --- a/bundles/core/org.eclipse.smarthome.core/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core/META-INF/MANIFEST.MF @@ -28,7 +28,7 @@ Ignore-Package: org.eclipse.smarthome.core.internal.items,org.eclipse.smarthome. nal,org.eclipse.smarthome.core.internal.events,org.eclipse.smarthome.core.internal.loggin g Bundle-Name: Eclipse SmartHome Core -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Vendor: Eclipse.org/SmartHome Bundle-Version: 0.9.0.qualifier Bundle-ManifestVersion: 2 @@ -38,12 +38,16 @@ Import-Package: com.google.common.base, com.google.gson, org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.core.auth, org.eclipse.smarthome.core.binding, org.eclipse.smarthome.core.binding.dto, + org.eclipse.smarthome.core.cache, org.eclipse.smarthome.core.common, org.eclipse.smarthome.core.common.osgi, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.extension, org.eclipse.smarthome.core.i18n, org.eclipse.smarthome.core.items, org.eclipse.smarthome.core.items.dto, @@ -51,6 +55,8 @@ Import-Package: com.google.common.base, org.eclipse.smarthome.core.library, org.eclipse.smarthome.core.library.items, org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.net, + org.eclipse.smarthome.core.scheduler, org.eclipse.smarthome.core.service, org.eclipse.smarthome.core.storage, org.eclipse.smarthome.core.types, diff --git a/bundles/core/org.eclipse.smarthome.core/OSGI-INF/.gitignore b/bundles/core/org.eclipse.smarthome.core/OSGI-INF/.gitignore new file mode 100644 index 00000000000..422f92361c5 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/org.eclipse.smarthome.network.xml diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/SafeMethodCaller.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/SafeMethodCaller.java index d927fe36090..147dcb678be 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/SafeMethodCaller.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/SafeMethodCaller.java @@ -119,14 +119,14 @@ public static V call(Action action, int timeout) { String className = stackTraceElement.getClassName(); String methodName = stackTraceElement.getMethodName(); - getLogger().error("Exception occurred while calling '" + methodName + "' at '" + className + "'", ex); + getLogger().error("Exception occurred while calling '{}' at '{}'", methodName, className, ex); } else { getLogger().error("Exception occurred while calling action", ex); } return null; } catch (TimeoutException ex) { getLogger().error( - "Timeout occurred while calling method. Execution took longer than " + timeout + " milliseconds.", + "Timeout occurred while calling method. Execution took longer than {} milliseconds.", timeout, ex); return null; } catch (Throwable throwable) { diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ResourceBundleClassLoader.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ResourceBundleClassLoader.java index 73bb7a27a30..7e818bf3a58 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ResourceBundleClassLoader.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ResourceBundleClassLoader.java @@ -15,7 +15,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; @@ -31,11 +31,10 @@ * * @author Michael Grammling - Initial Contribution * @author Martin Herbst - UTF-8 replaced by ISO-8859-1 to follow Java standards + * */ public class ResourceBundleClassLoader extends ClassLoader { - private static final Charset SUPPORTED_CHARSET = Charset.forName("ISO-8859-1"); - private Bundle bundle; private String path; private String filePattern; @@ -115,7 +114,7 @@ public InputStream getResourceAsStream(String name) { if (resourceURL != null) { try (InputStream resourceStream = resourceURL.openStream()) { if (resourceStream != null) { - try (Reader resourceReader = new InputStreamReader(resourceStream, SUPPORTED_CHARSET)) { + try (Reader resourceReader = new InputStreamReader(resourceStream, StandardCharsets.ISO_8859_1)) { Properties props = new Properties(); props.load(resourceReader); ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ServiceBinder.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ServiceBinder.java index 110dad45f71..efc15359c29 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ServiceBinder.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/osgi/ServiceBinder.java @@ -195,7 +195,7 @@ private void injectService(Method method, Object object, Object service) { method.invoke(object); } } catch (Exception ex) { - logger.error("Error while executing inject method: " + ex.getMessage(), ex); + logger.error("Error while executing inject method: {}", ex.getMessage(), ex); } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractManagedProvider.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractManagedProvider.java index 83614f11241..64072d9cba6 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractManagedProvider.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractManagedProvider.java @@ -9,6 +9,7 @@ import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.storage.Storage; import org.eclipse.smarthome.core.storage.StorageService; import org.slf4j.Logger; @@ -46,11 +47,6 @@ public abstract class AbstractManagedProvider, K, PE> @Override public void add(E element) { - - if (element == null) { - throw new IllegalArgumentException("Cannot add null element"); - } - String keyAsString = getKeyAsString(element); if (storage.get(keyAsString) != null) { throw new IllegalArgumentException( @@ -67,9 +63,13 @@ public Collection getAll() { final Function toElementList = new Function() { @Override public E apply(String elementKey) { - PE persistableElement = storage.get(elementKey); - if (persistableElement != null) { - return toElement(elementKey, persistableElement); + if (elementKey != null) { + PE persistableElement = storage.get(elementKey); + if (persistableElement != null) { + return toElement(elementKey, persistableElement); + } else { + return null; + } } else { return null; } @@ -101,11 +101,6 @@ public E get(K key) { @Override public E remove(K key) { - - if (key == null) { - throw new IllegalArgumentException("Cannot remove null element"); - } - String keyAsString = keyToString(key); PE persistableElement = storage.remove(keyAsString); if (persistableElement != null) { @@ -122,11 +117,6 @@ public E remove(K key) { @Override public E update(E element) { - - if (element == null) { - throw new IllegalArgumentException("Cannot update null element"); - } - String key = getKeyAsString(element); if (storage.get(key) != null) { PE persistableElement = storage.put(key, toPersistableElement(element)); @@ -142,19 +132,10 @@ public E update(E element) { return null; } - private String getKeyAsString(E element) { - return keyToString(getKey(element)); + private @NonNull String getKeyAsString(@NonNull E element) { + return keyToString(element.getUID()); } - /** - * Returns the key for a given element - * - * @param element - * element - * @return key (must not be null) - */ - protected abstract K getKey(E element); - /** * Returns the name of storage, that is used to persist the elements. * @@ -169,7 +150,7 @@ private String getKeyAsString(E element) { * key * @return string representation of the key */ - protected abstract String keyToString(K key); + protected abstract @NonNull String keyToString(@NonNull K key); protected void setStorageService(StorageService storageService) { this.storage = storageService.getStorage(getStorageName(), this.getClass().getClassLoader()); @@ -183,7 +164,7 @@ protected void setStorageService(StorageService storageService) { * persistable element * @return original element */ - protected abstract E toElement(String key, PE persistableElement); + protected abstract E toElement(@NonNull String key, @NonNull PE persistableElement); /** * Converts the original element into an element that can be persisted. diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractProvider.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractProvider.java index f35fce1da02..0a4bc9f67bc 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractProvider.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractProvider.java @@ -60,8 +60,7 @@ private void notifyListeners(E oldElement, E element, EventType eventType) { break; } } catch (Exception ex) { - logger.error("Could not inform the listener '" + listener + "' about the '" + eventType.name() - + "' event!: " + ex.getMessage(), ex); + logger.error("Could not inform the listener '{}' about the '{}' event!: {}", listener, eventType.name(), ex.getMessage(), ex); } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractRegistry.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractRegistry.java index 614dffb1ec7..1ca204fbc6b 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractRegistry.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/AbstractRegistry.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventPublisher; import org.osgi.framework.BundleContext; @@ -29,6 +30,7 @@ * @author Dennis Nobel - Initial contribution * @author Stefan Bußweiler - Migration to new event mechanism * @author Victor Toni - provide elements as {@link Stream} + * @author Kai Kreuzer - switched to parameterized logging * * @param * type of the element @@ -118,11 +120,17 @@ public void added(Provider provider, E element) { Collection elements = elementMap.get(provider); if (elements != null) { try { + K uid = element.getUID(); + if (uid != null && get(uid) != null) { + logger.warn("{} with key '{}' already exists! Failed to add a second with the same UID!", + element.getClass().getName(), uid); + return; + } onAddElement(element); elements.add(element); notifyListenersAboutAddedElement(element); } catch (Exception ex) { - logger.warn("Could not add element: " + ex.getMessage(), ex); + logger.warn("Could not add element: {}", ex.getMessage(), ex); } } } @@ -132,8 +140,9 @@ public void addRegistryChangeListener(RegistryChangeListener listener) { listeners.add(listener); } + @SuppressWarnings("null") @Override - public Collection getAll() { + public Collection<@NonNull E> getAll() { return stream().collect(Collectors.toList()); } @@ -153,7 +162,7 @@ public void removed(Provider provider, E element) { elements.remove(element); notifyListenersAboutRemovedElement(element); } catch (Exception ex) { - logger.warn("Could not remove element: " + ex.getMessage(), ex); + logger.warn("Could not remove element: {}", ex.getMessage(), ex); } } } @@ -166,16 +175,28 @@ public void removeRegistryChangeListener(RegistryChangeListener listener) { @Override public void updated(Provider provider, E oldElement, E element) { Collection elements = elementMap.get(provider); - if (elements != null) { + if (elements != null && elements.contains(oldElement) && oldElement.getUID().equals(element.getUID())) { try { onUpdateElement(oldElement, element); elements.remove(oldElement); elements.add(element); notifyListenersAboutUpdatedElement(oldElement, element); } catch (Exception ex) { - logger.warn("Could not update element: " + ex.getMessage(), ex); + logger.warn("Could not update element: {}", ex.getMessage(), ex); + } + } + } + + @Override + public E get(K key) { + for (final Map.Entry, Collection> entry : elementMap.entrySet()) { + for (final E element : entry.getValue()) { + if (key.equals(element.getUID())) { + return element; + } } } + return null; } @Override @@ -223,8 +244,8 @@ protected void notifyListeners(E oldElement, E element, EventType eventType) { break; } } catch (Throwable throwable) { - logger.error("Could not inform the listener '" + listener + "' about the '" + eventType.name() - + "' event!: " + throwable.getMessage(), throwable); + logger.error("Could not inform the listener '{}' about the '{}' event: {}", listener, eventType.name(), + throwable.getMessage(), throwable); } } } @@ -254,11 +275,17 @@ protected void addProvider(Provider provider) { elementMap.put(provider, elements); for (E element : elementsOfProvider) { try { + K uid = element.getUID(); + if (uid != null && get(uid) != null) { + logger.warn("{} with key'{}' already exists! Failed to add a second with the same UID!", + element.getClass().getName(), uid); + continue; + } onAddElement(element); elements.add(element); notifyListenersAboutAddedElement(element); } catch (Exception ex) { - logger.warn("Could not add element: " + ex.getMessage(), ex); + logger.warn("Could not add element: {}", ex.getMessage(), ex); } } logger.debug("Provider '{}' has been added.", provider.getClass().getName()); @@ -327,7 +354,7 @@ protected void removeProvider(Provider provider) { onRemoveElement(element); notifyListenersAboutRemovedElement(element); } catch (Exception ex) { - logger.warn("Could not remove element: " + ex.getMessage(), ex); + logger.warn("Could not remove element: {}", ex.getMessage(), ex); } } @@ -362,7 +389,7 @@ protected void postEvent(Event event) { try { eventPublisher.post(event); } catch (Exception ex) { - logger.error("Could not post event of type '" + event.getType() + "'.", ex); + logger.error("Could not post event of type '{}'.", event.getType(), ex); } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/ManagedProvider.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/ManagedProvider.java index 283deb61609..82fcd3c0eac 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/ManagedProvider.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/ManagedProvider.java @@ -7,6 +7,8 @@ */ package org.eclipse.smarthome.core.common.registry; +import org.eclipse.jdt.annotation.NonNull; + /** * The {@link ManagedProvider} is a specific {@link Provider} that enables to * add, remove and update elements at runtime. @@ -26,7 +28,7 @@ public interface ManagedProvider, K> extends Provider< * @param element * element to be added */ - void add(E element); + void add(@NonNull E element); /** * Removes an element and returns the removed element. @@ -36,7 +38,7 @@ public interface ManagedProvider, K> extends Provider< * @return element that was removed, or null if no element with the given * key exists */ - E remove(K key); + E remove(@NonNull K key); /** * Updates an element. @@ -46,7 +48,7 @@ public interface ManagedProvider, K> extends Provider< * @return returns the old element or null if no element with the same key * exists */ - E update(E element); + E update(@NonNull E element); /** * Returns an element for the given key or null if no element for the given diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/Registry.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/Registry.java index ff1958a2b7c..e2a72d56fb2 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/Registry.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/common/registry/Registry.java @@ -10,12 +10,16 @@ import java.util.Collection; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + /** * The {@link Registry} interface represents a registry for elements of the type * E. The concrete sub interfaces are registered as OSGi services. * * @author Dennis Nobel - Initial contribution * @author Victor Toni - provide elements as {@link Stream} + * @author Kai Kreuzer - added null annotations * * @param type of the elements in the registry */ @@ -26,14 +30,15 @@ public interface Registry, K> { * * @param listener registry change listener */ - void addRegistryChangeListener(RegistryChangeListener listener); + void addRegistryChangeListener(@NonNull RegistryChangeListener listener); /** * Returns a collection of all elements in the registry. * * @return collection of all elements in the registry */ - Collection getAll(); + @NonNull + Collection<@NonNull E> getAll(); /** * Returns a stream of all elements in the registry. @@ -49,7 +54,7 @@ public interface Registry, K> { * key of the element * @return element or null if no element was found */ - public E get(K key); + public @Nullable E get(K key); /** * Removes a {@link RegistryChangeListener} from the registry. @@ -57,7 +62,7 @@ public interface Registry, K> { * @param listener * registry change listener */ - void removeRegistryChangeListener(RegistryChangeListener listener); + void removeRegistryChangeListener(@NonNull RegistryChangeListener listener); /** * Adds the given element to the according {@link ManagedProvider}. @@ -68,7 +73,7 @@ public interface Registry, K> { * @throws IllegalStateException * if no ManagedProvider is available */ - public E add(E element); + public @NonNull E add(@NonNull E element); /** * Updates the given element at the according {@link ManagedProvider}. @@ -80,7 +85,7 @@ public interface Registry, K> { * @throws IllegalStateException * if no ManagedProvider is available */ - public E update(E element); + public @Nullable E update(@NonNull E element); /** * Removes the given element from the according {@link ManagedProvider}. @@ -92,5 +97,5 @@ public interface Registry, K> { * @throws IllegalStateException * if no ManagedProvider is available */ - public E remove(K key); + public @Nullable E remove(@NonNull K key); } \ No newline at end of file diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/i18n/I18nUtil.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/i18n/I18nUtil.java index 99dbc6ea441..66536cfad4d 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/i18n/I18nUtil.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/i18n/I18nUtil.java @@ -7,6 +7,10 @@ */ package org.eclipse.smarthome.core.i18n; +/** + * + * @author Denis Nobel - Initial contribution + */ public class I18nUtil { /** The 'text' pattern (prefix) which marks constants. */ diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/CoreActivator.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/CoreActivator.java index be666a4a307..2041b94d9aa 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/CoreActivator.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/CoreActivator.java @@ -14,6 +14,8 @@ /** * The activator class controls the plug-in life cycle + * + * @author Thomas Eichstädt-Engelen - Initial contribution */ public class CoreActivator implements BundleActivator { diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/events/OSGiEventManager.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/events/OSGiEventManager.java index 18ca5bd355d..5d9fd1dc9bd 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/events/OSGiEventManager.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/events/OSGiEventManager.java @@ -10,6 +10,7 @@ import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.util.Arrays; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; @@ -148,7 +149,7 @@ public void handleEvent(org.osgi.service.event.Event osgiEvent) { } else { logger.error( "The handled OSGi event is invalid. Expect properties as string named 'type', 'payload' and 'topic'. " - + "Received event properties are: " + osgiEvent.getPropertyNames()); + + "Received event properties are: {}", Arrays.toString(osgiEvent.getPropertyNames())); } } @@ -164,7 +165,7 @@ private void handleEvent(final String type, final String payload, final String t } } } else { - logger.warn("Could not find an Event Factory for the event type '" + type + "'."); + logger.warn("Could not find an Event Factory for the event type '{}'.", type); } } @@ -175,7 +176,7 @@ private Event createESHEvent(final EventFactory eventFactory, final String type, eshEvent = eventFactory.createEvent(type, topic, payload, source); } catch (Exception e) { logger.error("Creation of ESH-Event failed, " - + "because one of the registered event factories has thrown an exception: " + e.getMessage(), e); + + "because one of the registered event factories has thrown an exception: {}", e.getMessage(), e); } return eshEvent; } @@ -198,8 +199,8 @@ public Void call() throws Exception { logger.warn("Dispatching event to subscriber '{}' takes more than {}ms.", eventSubscriber.toString(), SafeMethodCaller.DEFAULT_TIMEOUT); } catch (Throwable t) { - logger.error("Dispatching/filtering event for subscriber '" + EventSubscriber.class.getName() - + "' failed: " + t.getMessage(), t); + logger.error("Dispatching/filtering event for subscriber '{}' failed: {}", + EventSubscriber.class.getName(), t.getMessage(), t); } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/i18n/ResourceBundleTracker.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/i18n/ResourceBundleTracker.java index 2c7e24e7c92..7716cf00f56 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/i18n/ResourceBundleTracker.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/i18n/ResourceBundleTracker.java @@ -140,15 +140,17 @@ private boolean isFragmentBundle(Bundle bundle) { } /** - * This method adds the localization resources provided by this OSGi bundle parameter, accordingly - * of that the resource bundle is detected. + * This method adds the localization resources provided by this OSGi bundle parameter if the bundle is not in + * UNINSTALLED state. * * @param bundle the OSGi bundle that was detected */ private void addResourceBundle(Bundle bundle) { - LanguageResourceBundleManager languageResource = new LanguageResourceBundleManager(localeProvider, bundle); - if (languageResource.containsResources()) { - this.bundleLanguageResourceMap.put(bundle, languageResource); + if (bundle.getState() != Bundle.UNINSTALLED) { + LanguageResourceBundleManager languageResource = new LanguageResourceBundleManager(localeProvider, bundle); + if (languageResource.containsResources()) { + this.bundleLanguageResourceMap.put(bundle, languageResource); + } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemRegistryImpl.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemRegistryImpl.java index 886961f9bfc..c3817a2c6a8 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemRegistryImpl.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemRegistryImpl.java @@ -12,10 +12,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Map; import org.eclipse.smarthome.core.common.registry.AbstractRegistry; -import org.eclipse.smarthome.core.common.registry.Provider; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.items.GroupItem; @@ -29,8 +27,6 @@ import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.types.StateDescriptionProvider; import org.osgi.service.component.ComponentContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * This is the main implementing class of the {@link ItemRegistry} interface. It @@ -44,8 +40,6 @@ */ public class ItemRegistryImpl extends AbstractRegistry implements ItemRegistry { - private final Logger logger = LoggerFactory.getLogger(ItemRegistryImpl.class); - private List stateDescriptionProviders = Collections .synchronizedList(new ArrayList()); @@ -63,18 +57,6 @@ public Item getItem(String name) throws ItemNotFoundException { } } - @Override - public Item get(final String itemName) { - for (final Map.Entry, Collection> entry : elementMap.entrySet()) { - for (final Item item : entry.getValue()) { - if (itemName.equals(item.getName())) { - return item; - } - } - } - return null; - } - @Override public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotUniqueException { Collection items = getItems(name); @@ -87,8 +69,13 @@ public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotU throw new ItemNotUniqueException(name, items); } - return items.iterator().next(); + Item item = items.iterator().next(); + if (item == null) { + throw new ItemNotFoundException(name); + } else { + return item; + } } @Override @@ -125,13 +112,15 @@ public Collection getItems(String pattern) { private void addToGroupItems(Item item, List groupItemNames) { for (String groupName : groupItemNames) { - try { - Item groupItem = getItem(groupName); - if (groupItem instanceof GroupItem) { - ((GroupItem) groupItem).addMember(item); + if (groupName != null) { + try { + Item groupItem = getItem(groupName); + if (groupItem instanceof GroupItem) { + ((GroupItem) groupItem).addMember(item); + } + } catch (ItemNotFoundException e) { + // the group might not yet be registered, let's ignore this } - } catch (ItemNotFoundException e) { - // the group might not yet be registered, let's ignore this } } } @@ -184,13 +173,15 @@ private void addMembersToGroupItem(GroupItem groupItem) { private void removeFromGroupItems(Item item, List groupItemNames) { for (String groupName : groupItemNames) { - try { - Item groupItem = getItem(groupName); - if (groupItem instanceof GroupItem) { - ((GroupItem) groupItem).removeMember(item); + if (groupName != null) { + try { + Item groupItem = getItem(groupName); + if (groupItem instanceof GroupItem) { + ((GroupItem) groupItem).removeMember(item); + } + } catch (ItemNotFoundException e) { + // the group might not yet be registered, let's ignore this } - } catch (ItemNotFoundException e) { - // the group might not yet be registered, let's ignore this } } } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemUpdater.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemUpdater.java index 77a93464267..8832630cdf5 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemUpdater.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/internal/items/ItemUpdater.java @@ -69,8 +69,8 @@ protected void receiveUpdate(ItemStateEvent updateEvent) { if (isAccepted) { item.setState(newState); } else { - logger.debug("Received update of a not accepted type (" + newState.getClass().getSimpleName() - + ") for item " + itemName); + logger.debug("Received update of a not accepted type ({}) for item {}", + newState.getClass().getSimpleName(), itemName); } } catch (ItemNotFoundException e) { logger.debug("Received update for non-existing item: {}", e.getMessage()); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GenericItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GenericItem.java index 3749badc309..4ae84e143cd 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GenericItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GenericItem.java @@ -19,6 +19,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.items.events.ItemEventFactory; @@ -27,6 +28,7 @@ import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.StateDescription; import org.eclipse.smarthome.core.types.StateDescriptionProvider; +import org.eclipse.smarthome.core.types.StateOption; import org.eclipse.smarthome.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,8 +62,10 @@ abstract public class GenericItem implements ActiveItem { protected Set tags = new HashSet(); + @NonNull final protected String name; + @NonNull final protected String type; protected State state = UnDefType.NULL; @@ -72,7 +76,7 @@ abstract public class GenericItem implements ActiveItem { private List stateDescriptionProviders; - public GenericItem(String type, String name) { + public GenericItem(@NonNull String type, @NonNull String name) { this.name = name; this.type = type; } @@ -315,11 +319,7 @@ public boolean equals(Object obj) { } else if (!label.equals(other.label)) { return false; } - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { + if (!name.equals(other.name)) { return false; } if (tags == null) { @@ -329,11 +329,7 @@ public boolean equals(Object obj) { } else if (!tags.equals(other.tags)) { return false; } - if (type == null) { - if (other.type != null) { - return false; - } - } else if (!type.equals(other.type)) { + if (!type.equals(other.type)) { return false; } return true; @@ -401,15 +397,32 @@ public StateDescription getStateDescription() { @Override public StateDescription getStateDescription(Locale locale) { + StateDescription result = null; + List stateOptions = Collections.emptyList(); if (stateDescriptionProviders != null) { for (StateDescriptionProvider stateDescriptionProvider : stateDescriptionProviders) { StateDescription stateDescription = stateDescriptionProvider.getStateDescription(this.name, locale); - if (stateDescription != null) { - return stateDescription; + + // as long as no valid StateDescription is provided we reassign here: + if (result == null) { + result = stateDescription; + } + + // if the current StateDescription does provide options and we don't already have some, we pick them up + // here + if (stateDescription != null && !stateDescription.getOptions().isEmpty() && stateOptions.isEmpty()) { + stateOptions = stateDescription.getOptions(); } } } - return null; + + // we recreate the StateDescription if we found a valid one and state options are given: + if (result != null && !stateOptions.isEmpty()) { + result = new StateDescription(result.getMinimum(), result.getMaximum(), result.getStep(), + result.getPattern(), result.isReadOnly(), stateOptions); + } + + return result; } /** diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GroupItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GroupItem.java index 22f776e0fc3..0a8665601e8 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GroupItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/GroupItem.java @@ -16,6 +16,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; @@ -25,8 +26,13 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class GroupItem extends GenericItem implements StateChangeListener { + @NonNull public static final String TYPE = "Group"; private final Logger logger = LoggerFactory.getLogger(GroupItem.class); @@ -42,11 +48,11 @@ public class GroupItem extends GenericItem implements StateChangeListener { * * @param name name of the group */ - public GroupItem(String name) { + public GroupItem(@NonNull String name) { this(name, null, null); } - public GroupItem(String name, GenericItem baseItem) { + public GroupItem(@NonNull String name, GenericItem baseItem) { // only baseItem but no function set -> use Equality this(name, baseItem, new GroupFunction.Equality()); } @@ -58,7 +64,7 @@ public GroupItem(String name, GenericItem baseItem) { * @param baseItem type of items in the group * @param function function to calculate group status out of member status */ - public GroupItem(String name, GenericItem baseItem, GroupFunction function) { + public GroupItem(@NonNull String name, GenericItem baseItem, GroupFunction function) { super(TYPE, name); // we only allow GroupItem with BOTH, baseItem AND function set, or NONE of them set diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/Item.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/Item.java index 06003b9b03a..daa64cd78b9 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/Item.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/Item.java @@ -11,6 +11,7 @@ import java.util.Locale; import java.util.Set; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.common.registry.Identifiable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; @@ -51,14 +52,14 @@ public interface Item extends Identifiable { * * @return the name of the item */ - public String getName(); + public @NonNull String getName(); /** * returns the item type as defined by {@link ItemFactory}s * * @return the item type */ - public String getType(); + public @NonNull String getType(); /** *

@@ -128,14 +129,16 @@ public interface Item extends Identifiable { public String getCategory(); /** - * Returns the state description (uses the default locale). + * Returns the first provided state description (uses the default locale). + * If options are defined on the channel, they are included in the returned state description. * * @return state description (can be null) */ public StateDescription getStateDescription(); /** - * Returns the state description for a given locale. + * Returns the first provided state description for a given locale. + * If options are defined on the channel, they are included in the returned state description. * * @param locale * locale (can be null) diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemFactory.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemFactory.java index ffbe6ac22a7..6979ad87719 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemFactory.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemFactory.java @@ -7,6 +7,8 @@ */ package org.eclipse.smarthome.core.items; +import org.eclipse.jdt.annotation.NonNull; + /** * This Factory creates concrete instances of the known ItemTypes. * @@ -16,17 +18,17 @@ public interface ItemFactory { /** * Creates a new Item instance of type itemTypeName and the name itemName - * + * * @param itemTypeName * @param itemName - * + * * @return a new Item of type itemTypeName or null if no matching class is known. */ - GenericItem createItem(String itemTypeName, String itemName); + GenericItem createItem(@NonNull String itemTypeName, @NonNull String itemName); /** * Returns the list of all supported ItemTypes of this Factory. - * + * * @return the supported ItemTypes */ String[] getSupportedItemTypes(); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemRegistry.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemRegistry.java index 8d17295d48c..f63b0b6f54c 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemRegistry.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ItemRegistry.java @@ -9,6 +9,8 @@ import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.common.registry.Registry; /** @@ -31,7 +33,7 @@ public interface ItemRegistry extends Registry { * @return the uniquely identified item * @throws ItemNotFoundException if no item matches the input */ - public Item getItem(String name) throws ItemNotFoundException; + public @NonNull Item getItem(String name) throws ItemNotFoundException; /** * This method retrieves a single item from the registry. @@ -42,14 +44,14 @@ public interface ItemRegistry extends Registry { * @throws ItemNotFoundException if no item matches the input * @throws ItemNotUniqueException if multiply items match the input */ - public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotUniqueException; + public @NonNull Item getItemByPattern(@NonNull String name) throws ItemNotFoundException, ItemNotUniqueException; /** * This method retrieves all items that are currently available in the registry * * @return a collection of all available items */ - public Collection getItems(); + public @NonNull Collection<@NonNull Item> getItems(); /** * This method retrieves all items with the given type @@ -58,14 +60,14 @@ public interface ItemRegistry extends Registry { * - item type as defined by {@link ItemFactory}s * @return a collection of all items of the given type */ - public Collection getItemsOfType(String type); + public @NonNull Collection getItemsOfType(@NonNull String type); /** * This method retrieves all items that match a given search pattern * * @return a collection of all items matching the search pattern */ - public Collection getItems(String pattern); + public @NonNull Collection<@NonNull Item> getItems(@NonNull String pattern); /** * Returns list of items which contains all of the given tags. @@ -74,7 +76,7 @@ public interface ItemRegistry extends Registry { * - array of tags to be present on the returned items. * @return list of items which contains all of the given tags. */ - public Collection getItemsByTag(String... tags); + public @NonNull Collection getItemsByTag(@NonNull String... tags); /** * Returns list of items with a certain type containing all of the given tags. @@ -85,7 +87,7 @@ public interface ItemRegistry extends Registry { * - array of tags to be present on the returned items. * @return list of items which contains all of the given tags. */ - public Collection getItemsByTagAndType(String type, String... tags); + public @NonNull Collection getItemsByTagAndType(@NonNull String type, @NonNull String... tags); /** * Returns list of items which contains all of the given tags. @@ -98,11 +100,12 @@ public interface ItemRegistry extends Registry { * @return list of items which contains all of the given tags, which is * filtered by the given type filter. */ - public Collection getItemsByTag(Class typeFilter, String... tags); + public @NonNull Collection getItemsByTag(@NonNull Class typeFilter, + @NonNull String... tags); /** * @see ManagedItemProvider#remove(String, boolean) */ - public Item remove(String itemName, boolean recursive); + public @Nullable Item remove(@NonNull String itemName, boolean recursive); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ManagedItemProvider.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ManagedItemProvider.java index 7818e11d856..86c07b25cf1 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ManagedItemProvider.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/ManagedItemProvider.java @@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.common.registry.AbstractManagedProvider; import org.eclipse.smarthome.core.items.ManagedItemProvider.PersistedItem; import org.eclipse.smarthome.core.items.dto.GroupFunctionDTO; @@ -43,11 +44,15 @@ public class ManagedItemProvider extends AbstractManagedProvider groupNames; - public String itemType; + public @NonNull String itemType; public Set tags; @@ -61,7 +66,7 @@ public static class PersistedItem { } - private static final String ITEM_TYPE_GROUP = "Group"; + private static final @NonNull String ITEM_TYPE_GROUP = "Group"; private final Logger logger = LoggerFactory.getLogger(ManagedItemProvider.class); @@ -82,8 +87,7 @@ public static class PersistedItem { public Item remove(String itemName, boolean recursive) { Item item = get(itemName); if (recursive && item instanceof GroupItem) { - List members = getMemberNamesRecursively((GroupItem) item, getAll()); - for (String member : members) { + for (String member : getMemberNamesRecursively((GroupItem) item, getAll())) { this.remove(member); } } @@ -95,8 +99,8 @@ public Item remove(String itemName, boolean recursive) { } } - private List getMemberNamesRecursively(GroupItem groupItem, Collection allItems) { - List memberNames = new ArrayList<>(); + private List<@NonNull String> getMemberNamesRecursively(GroupItem groupItem, Collection allItems) { + List<@NonNull String> memberNames = new ArrayList<>(); for (Item item : allItems) { if (item.getGroupNames().contains(groupItem.getName())) { memberNames.add(item.getName()); @@ -108,7 +112,7 @@ private List getMemberNamesRecursively(GroupItem groupItem, Collection entry = iterator.next(); String itemName = entry.getKey(); PersistedItem persistedItem = entry.getValue(); + @SuppressWarnings("null") ActiveItem item = itemFactory.createItem(persistedItem.itemType, itemName); if (item != null) { iterator.remove(); @@ -160,11 +165,6 @@ protected void addItemFactory(ItemFactory itemFactory) { } } - @Override - protected String getKey(Item element) { - return element.getName(); - } - @Override protected String getStorageName() { return Item.class.getName(); @@ -243,7 +243,8 @@ private void configureItem(PersistedItem persistedItem, ActiveItem item) { @Override protected PersistedItem toPersistableElement(Item item) { - PersistedItem persistedItem = new PersistedItem(); + PersistedItem persistedItem = new PersistedItem( + item instanceof GroupItem ? ITEM_TYPE_GROUP : toItemFactoryName(item)); if (item instanceof GroupItem) { GroupItem groupItem = (GroupItem) item; @@ -252,13 +253,9 @@ protected PersistedItem toPersistableElement(Item item) { if (baseItem != null) { baseItemType = toItemFactoryName(baseItem); } - persistedItem.itemType = ITEM_TYPE_GROUP; persistedItem.baseItemType = baseItemType; addFunctionToPersisedItem(persistedItem, groupItem); - } else { - String itemType = toItemFactoryName(item); - persistedItem.itemType = itemType; } persistedItem.label = item.getLabel(); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/dto/ItemDTOMapper.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/dto/ItemDTOMapper.java index 41b8a534f8e..32d3a00d6a0 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/dto/ItemDTOMapper.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/dto/ItemDTOMapper.java @@ -156,7 +156,7 @@ public static GroupFunction mapFunction(Item baseItem, GroupFunctionDTO function break; default: LoggerFactory.getLogger(ItemDTOMapper.class) - .error("Unknown group function '" + function.name + "'. Using Equality instead."); + .error("Unknown group function '{}'. Using Equality instead.", function.name); } if (groupFunction == null) { diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/CoreItemFactory.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/CoreItemFactory.java index fe34d0a7f46..6d18fa90da1 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/CoreItemFactory.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/CoreItemFactory.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.core.library; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.items.ItemFactory; import org.eclipse.smarthome.core.library.items.CallItem; @@ -31,25 +32,21 @@ */ public class CoreItemFactory implements ItemFactory { - public static final String CALL = "Call"; - public static final String COLOR = "Color"; - public static final String CONTACT = "Contact"; - public static final String DATETIME = "DateTime"; - public static final String DIMMER = "Dimmer"; - public static final String IMAGE = "Image"; - public static final String LOCATION = "Location"; - public static final String NUMBER = "Number"; - public static final String PLAYER = "Player"; - public static final String ROLLERSHUTTER = "Rollershutter"; - public static final String STRING = "String"; - public static final String SWITCH = "Switch"; + public static final @NonNull String CALL = "Call"; + public static final @NonNull String COLOR = "Color"; + public static final @NonNull String CONTACT = "Contact"; + public static final @NonNull String DATETIME = "DateTime"; + public static final @NonNull String DIMMER = "Dimmer"; + public static final @NonNull String IMAGE = "Image"; + public static final @NonNull String LOCATION = "Location"; + public static final @NonNull String NUMBER = "Number"; + public static final @NonNull String PLAYER = "Player"; + public static final @NonNull String ROLLERSHUTTER = "Rollershutter"; + public static final @NonNull String STRING = "String"; + public static final @NonNull String SWITCH = "Switch"; @Override public GenericItem createItem(String itemTypeName, String itemName) { - if (itemTypeName == null) { - return null; - } - switch (itemTypeName) { case CALL: return new CallItem(itemName); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/internal/CoreLibraryActivator.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/internal/CoreLibraryActivator.java index 78b5adb09a7..eb9ef02377b 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/internal/CoreLibraryActivator.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/internal/CoreLibraryActivator.java @@ -10,6 +10,10 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class CoreLibraryActivator implements BundleActivator { @Override diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/CallItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/CallItem.java index c7d164df3f0..f3591d7fe42 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/CallItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/CallItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.StringListType; @@ -34,7 +35,7 @@ public class CallItem extends GenericItem { acceptedDataTypes.add(UnDefType.class); } - public CallItem(String name) { + public CallItem(@NonNull String name) { super(CoreItemFactory.CALL, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ColorItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ColorItem.java index 58bb101e6f7..49a33b32db6 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ColorItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ColorItem.java @@ -12,6 +12,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.HSBType; @@ -47,7 +48,7 @@ public class ColorItem extends DimmerItem { acceptedCommandTypes.add(RefreshType.class); } - public ColorItem(String name) { + public ColorItem(@NonNull String name) { super(CoreItemFactory.COLOR, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ContactItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ContactItem.java index 2b882efb388..86ae6941cd1 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ContactItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ContactItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.OpenClosedType; @@ -38,7 +39,7 @@ public class ContactItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public ContactItem(String name) { + public ContactItem(@NonNull String name) { super(CoreItemFactory.CONTACT, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DateTimeItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DateTimeItem.java index 61f49465fda..e3058de43ed 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DateTimeItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DateTimeItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.DateTimeType; @@ -39,7 +40,7 @@ public class DateTimeItem extends GenericItem { acceptedCommandTypes.add(DateTimeType.class); } - public DateTimeItem(String name) { + public DateTimeItem(@NonNull String name) { super(CoreItemFactory.DATETIME, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DimmerItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DimmerItem.java index 02d053e2570..cde75266745 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DimmerItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/DimmerItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -44,11 +45,11 @@ public class DimmerItem extends SwitchItem { acceptedCommandTypes.add(RefreshType.class); } - public DimmerItem(String name) { + public DimmerItem(@NonNull String name) { super(CoreItemFactory.DIMMER, name); } - /* package */ DimmerItem(String type, String name) { + /* package */ DimmerItem(@NonNull String type, @NonNull String name) { super(type, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ImageItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ImageItem.java index 1b174964943..b8eed49287b 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ImageItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/ImageItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.RawType; @@ -37,7 +38,7 @@ public class ImageItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public ImageItem(String name) { + public ImageItem(@NonNull String name) { super(CoreItemFactory.IMAGE, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/LocationItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/LocationItem.java index 83cb32e00eb..2a9b7eeab26 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/LocationItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/LocationItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.DecimalType; @@ -40,7 +41,7 @@ public class LocationItem extends GenericItem { acceptedCommandTypes.add(PointType.class); } - public LocationItem(String name) { + public LocationItem(@NonNull String name) { super(CoreItemFactory.LOCATION, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/NumberItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/NumberItem.java index 8096bd6a80f..2800ea68e8d 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/NumberItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/NumberItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.DecimalType; @@ -41,7 +42,7 @@ public class NumberItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public NumberItem(String name) { + public NumberItem(@NonNull String name) { super(CoreItemFactory.NUMBER, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/PlayerItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/PlayerItem.java index 4d75fca41bf..8ac93e6f1b7 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/PlayerItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/PlayerItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.NextPreviousType; @@ -42,11 +43,11 @@ public class PlayerItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public PlayerItem(String name) { + public PlayerItem(@NonNull String name) { super(CoreItemFactory.PLAYER, name); } - /* package */ PlayerItem(String type, String name) { + /* package */ PlayerItem(@NonNull String type, @NonNull String name) { super(type, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/RollershutterItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/RollershutterItem.java index fc3314dc118..b92d57fdb10 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/RollershutterItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/RollershutterItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.PercentType; @@ -46,7 +47,7 @@ public class RollershutterItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public RollershutterItem(String name) { + public RollershutterItem(@NonNull String name) { super(CoreItemFactory.ROLLERSHUTTER, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/StringItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/StringItem.java index 22cb94f7ebd..599fc28cffc 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/StringItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/StringItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.DateTimeType; @@ -42,7 +43,7 @@ public class StringItem extends GenericItem { acceptedCommandTypes.add(StringType.class); } - public StringItem(String name) { + public StringItem(@NonNull String name) { super(CoreItemFactory.STRING, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/SwitchItem.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/SwitchItem.java index fe1da4e3245..d605286d312 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/SwitchItem.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/items/SwitchItem.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -39,11 +40,11 @@ public class SwitchItem extends GenericItem { acceptedCommandTypes.add(RefreshType.class); } - public SwitchItem(String name) { + public SwitchItem(@NonNull String name) { super(CoreItemFactory.SWITCH, name); } - /* package */ SwitchItem(String type, String name) { + /* package */ SwitchItem(@NonNull String type, @NonNull String name) { super(type, name); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/DateTimeType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/DateTimeType.java index cd830992a90..48622125e4c 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/DateTimeType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/DateTimeType.java @@ -17,6 +17,10 @@ import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.State; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class DateTimeType implements PrimitiveType, State, Command { public static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/IncreaseDecreaseType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/IncreaseDecreaseType.java index 3634c9d90e5..63224b16366 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/IncreaseDecreaseType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/IncreaseDecreaseType.java @@ -10,6 +10,10 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.PrimitiveType; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public enum IncreaseDecreaseType implements PrimitiveType, Command { INCREASE, DECREASE; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OnOffType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OnOffType.java index e1e2eb4c91f..4dba17a3915 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OnOffType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OnOffType.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.State; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public enum OnOffType implements PrimitiveType, State, Command { ON, OFF; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OpenClosedType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OpenClosedType.java index b2fa40113eb..644961c91a0 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OpenClosedType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/OpenClosedType.java @@ -11,6 +11,10 @@ import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.State; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public enum OpenClosedType implements PrimitiveType, State, Command { OPEN, CLOSED; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StopMoveType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StopMoveType.java index 524c4873f76..b89ed29fb57 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StopMoveType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StopMoveType.java @@ -10,6 +10,10 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.PrimitiveType; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public enum StopMoveType implements PrimitiveType, Command { STOP, MOVE; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringListType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringListType.java index 72db5451955..f0121770b91 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringListType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringListType.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.Formatter; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; import org.eclipse.smarthome.core.types.Command; @@ -43,9 +42,7 @@ public StringListType(List rows) { } public StringListType(StringType... rows) { - typeDetails = Arrays.stream(rows) - .map(StringType::toString) - .collect(Collectors.toList()); + typeDetails = Arrays.stream(rows).map(StringType::toString).collect(Collectors.toList()); } public StringListType(String... rows) { @@ -56,9 +53,7 @@ public StringListType(String... rows) { * Deserialize the input string, splitting it on every delimiter not preceded by a backslash. */ public StringListType(String serialized) { - typeDetails = Arrays.stream( - serialized.split(REGEX_SPLITTER)) - .map(s -> s.replace(ESCAPED_DELIMITER, DELIMITER)) + typeDetails = Arrays.stream(serialized.split(REGEX_SPLITTER)).map(s -> s.replace(ESCAPED_DELIMITER, DELIMITER)) .collect(Collectors.toList()); } @@ -91,8 +86,7 @@ public String toString() { @Override public String toFullString() { - return typeDetails.stream() - .map(s -> s.replace(DELIMITER, ESCAPED_DELIMITER)) + return typeDetails.stream().map(s -> s.replace(DELIMITER, ESCAPED_DELIMITER)) .collect(Collectors.joining(DELIMITER)); } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringType.java index 9bcf98ecbab..6a0c286529b 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/StringType.java @@ -13,6 +13,10 @@ import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.State; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class StringType implements PrimitiveType, State, Command { public final static StringType EMPTY = new StringType(); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/UpDownType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/UpDownType.java index 9e464c19fcf..cf0edbc8bae 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/UpDownType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/library/types/UpDownType.java @@ -13,6 +13,10 @@ import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.State; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public enum UpDownType implements PrimitiveType, State, Command { UP, DOWN; diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetUtil.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetUtil.java index b5785cf7958..bbc652eff82 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetUtil.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetUtil.java @@ -16,7 +16,13 @@ import java.util.Enumeration; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNull; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Modified; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,17 +31,63 @@ * * @author Markus Rathgeb - Initial contribution and API * @author Mark Herwege - Added methods to find broadcast address(es) + * @author Stefan Triller - Converted to OSGi service with primary ipv4 conf */ -public class NetUtil { +@Component(name = "org.eclipse.smarthome.network", property = { "service.config.description.uri=system:network", + "service.config.label=Network Settings", "service.config.category=system" }) +public class NetUtil implements NetworkAddressService { + private static final String PRIMARY_ADDRESS = "primaryAddress"; private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class); - private NetUtil() { + private static final Pattern IPV4_PATTERN = Pattern + .compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); + + private String primaryAddress; + + @Activate + protected void activate(Map props) { + modified(props); + } + + @Modified + public synchronized void modified(Map config) { + String primaryAddressConf = (String) config.get(PRIMARY_ADDRESS); + if (primaryAddressConf == null || primaryAddressConf.isEmpty() || !isValidIPConfig(primaryAddressConf)) { + // if none is specified we return the default one for backward compatibility + primaryAddress = getFirstLocalIPv4Address(); + } else { + primaryAddress = primaryAddressConf; + } + } + + @Override + public String getPrimaryIpv4HostAddress() { + String primaryIP; + + String[] addrString = primaryAddress.split("/"); + if (addrString.length > 1) { + String ip = getIPv4inSubnet(primaryAddress); + if (ip == null) { + // an error has occurred, using first interface like nothing has been configured + LOGGER.warn("Invalid address '{}', will use first interface instead.", primaryAddress); + primaryIP = getFirstLocalIPv4Address(); + } else { + primaryIP = ip; + } + } else { + primaryIP = addrString[0]; + } + + return primaryIP; } /** + * Deprecated: Please use the NetworkAddressService with getPrimaryIpv4HostAddress() + * * Get the first candidate for a local IPv4 host address (non loopback, non localhost). */ + @Deprecated public static String getLocalIpv4HostAddress() { try { String hostAddress = null; @@ -65,6 +117,35 @@ public static String getLocalIpv4HostAddress() { } } + private String getFirstLocalIPv4Address() { + try { + String hostAddress = null; + final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + final NetworkInterface current = interfaces.nextElement(); + if (!current.isUp() || current.isLoopback() || current.isVirtual()) { + continue; + } + final Enumeration addresses = current.getInetAddresses(); + while (addresses.hasMoreElements()) { + final InetAddress current_addr = addresses.nextElement(); + if (current_addr.isLoopbackAddress() || (current_addr instanceof Inet6Address)) { + continue; + } + if (hostAddress != null) { + LOGGER.warn("Found multiple local interfaces - ignoring {}", current_addr.getHostAddress()); + } else { + hostAddress = current_addr.getHostAddress(); + } + } + } + return hostAddress; + } catch (SocketException ex) { + LOGGER.error("Could not retrieve network interface: {}", ex.getMessage(), ex); + return null; + } + } + /** * Get all broadcast addresses on the current host * @@ -104,4 +185,130 @@ public static String getBroadcastAddress() { } } + /** + * Converts a netmask in bits into a string representation + * i.e. 24 bits -> 255.255.255.0 + * + * @param prefixLength bits of the netmask + * @return string representation of netmask (i.e. 255.255.255.0) + */ + public static @NonNull String networkPrefixLengthToNetmask(int prefixLength) { + if (prefixLength > 31 || prefixLength < 1) { + throw new IllegalArgumentException("Network prefix length is not within bounds"); + } + + int ipv4Netmask = 0xFFFFFFFF; + ipv4Netmask <<= (32 - prefixLength); + + byte[] octets = new byte[] { (byte) (ipv4Netmask >>> 24), (byte) (ipv4Netmask >>> 16), + (byte) (ipv4Netmask >>> 8), (byte) ipv4Netmask }; + + String result = ""; + for (int i = 0; i < 4; i++) { + result += octets[i] & 0xff; + if (i < 3) { + result += "."; + } + } + return result; + } + + /** + * Get the network address a specific ip address is in + * + * @param ipAddressString ipv4 address of the device (i.e. 192.168.5.1) + * @param netMask netmask in bits (i.e. 24) + * @return network a device is in (i.e. 192.168.5.0) + * + * @throws IllegalArgumentException if parameters are wrong + */ + public static @NonNull String getIpv4NetAddress(@NonNull String ipAddressString, short netMask) { + + String errorString = "IP '" + ipAddressString + "' is not a valid IPv4 address"; + if (!isValidIPConfig(ipAddressString)) { + throw new IllegalArgumentException(errorString); + } + if (netMask < 1 || netMask > 31) { + throw new IllegalArgumentException("Netmask '" + netMask + "' is out of bounds (1-31)"); + } + + String subnetMaskString = networkPrefixLengthToNetmask(netMask); + + String[] netMaskOctets = subnetMaskString.split("\\."); + String[] ipv4AddressOctets = ipAddressString.split("\\."); + String netAddress = ""; + try { + for (int i = 0; i < 4; i++) { + netAddress += Integer.parseInt(ipv4AddressOctets[i]) & Integer.parseInt(netMaskOctets[i]); + if (i < 3) { + netAddress += "."; + } + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException(errorString); + } + + return netAddress; + } + + private String getIPv4inSubnet(String subnet) { + try { + final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + final NetworkInterface current = interfaces.nextElement(); + if (!current.isUp() || current.isLoopback() || current.isVirtual()) { + continue; + } + + for (InterfaceAddress ifAddr : current.getInterfaceAddresses()) { + InetAddress addr = ifAddr.getAddress(); + + if (addr.isLoopbackAddress() || (addr instanceof Inet6Address)) { + continue; + } + + String ipv4Address = addr.getHostAddress(); + String subNetString = getIpv4NetAddress(ipv4Address, ifAddr.getNetworkPrefixLength()) + "/" + + String.valueOf(ifAddr.getNetworkPrefixLength()); + + // use first IP within this subnet + if (subNetString.equals(subnet)) { + return ipv4Address; + } + } + } + } catch (SocketException ex) { + LOGGER.error("Could not retrieve network interface: {}", ex.getMessage(), ex); + } + return null; + } + + /** + * Checks if the given String is a valid IPv4 Address + * or IPv4 address in CIDR notation + * + * @param ipAddress in format xxx.xxx.xxx.xxx or xxx.xxx.xxx.xxx/xx + * @return true if it is a valid address + */ + public static boolean isValidIPConfig(String ipAddress) { + + if (ipAddress.contains("/")) { + String parts[] = ipAddress.split("/"); + boolean ipMatches = IPV4_PATTERN.matcher(parts[0]).matches(); + + int netMask = Integer.parseInt(parts[1]); + boolean netMaskMatches = false; + if (netMask > 0 || netMask < 32) { + netMaskMatches = true; + } + + if (ipMatches && netMaskMatches) { + return true; + } + } else { + return IPV4_PATTERN.matcher(ipAddress).matches(); + } + return false; + } + } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetworkAddressService.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetworkAddressService.java new file mode 100644 index 00000000000..7dd687efbb8 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/net/NetworkAddressService.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.core.net; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * Interface that provides access to configured network addresses + * + * @author Stefan Triller - initial contribution + * + */ +public interface NetworkAddressService { + + /** + * Returns the user configured primary IPv4 address of the system + * + * @return IPv4 address as a String in format xxx.xxx.xxx.xxx or + * null if there is no interface or an error occurred + */ + @Nullable + String getPrimaryIpv4HostAddress(); +} diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/scheduler/ExpressionThreadPoolManager.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/scheduler/ExpressionThreadPoolManager.java index 4926dab82be..8cb9ff69bd5 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/scheduler/ExpressionThreadPoolManager.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/scheduler/ExpressionThreadPoolManager.java @@ -111,16 +111,16 @@ protected void afterExecute(Runnable runnable, Throwable throwable) { super.afterExecute(runnable, throwable); if (runnable instanceof Future) { + Future future = (Future) runnable; try { futuresLock.lock(); for (Runnable aRunnable : futures.keySet()) { - futures.get(aRunnable).removeIf(future -> future == runnable); + futures.get(aRunnable).removeIf(entry -> entry == future); } } finally { futuresLock.unlock(); } - timestamps.remove(runnable); - + timestamps.remove(future); } else { List> obsoleteFutures = new ArrayList>(); try { diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/WatchQueueReader.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/WatchQueueReader.java index bc9bf39a873..7f7d2943d40 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/WatchQueueReader.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/WatchQueueReader.java @@ -23,7 +23,6 @@ import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributes; -import java.text.MessageFormat; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedList; @@ -180,16 +179,15 @@ public void run() { try { key = watchService.take(); } catch (InterruptedException exc) { - logger.info(MessageFormat.format("Caught InterruptedException: {0}", exc.getLocalizedMessage())); + logger.info("Caught InterruptedException: {}", exc.getLocalizedMessage()); return; } for (WatchEvent event : key.pollEvents()) { WatchEvent.Kind kind = event.kind(); if (kind == OVERFLOW) { - logger.warn(MessageFormat.format( - "Found an event of kind 'OVERFLOW': {0}. File system changes might have been missed.", - event)); + logger.warn("Found an event of kind 'OVERFLOW': {}. File system changes might have been missed.", + event); continue; } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/storage/Storage.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/storage/Storage.java index 28cb250d621..41a2f2af04d 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/storage/Storage.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/storage/Storage.java @@ -9,6 +9,9 @@ import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + /** * A Storage is the generic way to store key-value pairs in ESH. Each Storage * implementation can store its data differently, e.g in-memory or in-database. @@ -20,12 +23,13 @@ public interface Storage { /** * Puts a key-value mapping into this Storage. - * + * * @param key the key to add * @param value the value to add * @return previous value for the key or null if no value was replaced */ - T put(String key, T value); + @Nullable + T put(@NonNull String key, T value); /** * Removes the specified mapping from this map. @@ -33,11 +37,12 @@ public interface Storage { * @param key the mapping to remove * @return the removed value or null if no entry existed */ - T remove(String key); + @Nullable + T remove(@NonNull String key); /** * Gets the value mapped to the key specified. - * + * * @param key the key * @return the mapped value, null if no match */ @@ -45,14 +50,14 @@ public interface Storage { /** * Gets all keys of this Storage. - * + * * @return the keys of this Storage */ Collection getKeys(); /** * Gets all values of this Storage. - * + * * @return the values of this Storage */ Collection getValues(); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/types/RefreshType.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/types/RefreshType.java index 94c44ee1e1a..48e66b14d8a 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/types/RefreshType.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/types/RefreshType.java @@ -7,6 +7,10 @@ */ package org.eclipse.smarthome.core.types; +/** + * + * @author Oliver Libutzki - Initial contribution + */ public enum RefreshType implements PrimitiveType, Command { REFRESH; diff --git a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/CoreActivator.java b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/CoreActivator.java index 207cf0ac46d..13f6f199bcc 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/CoreActivator.java +++ b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/CoreActivator.java @@ -21,6 +21,8 @@ /** * The activator class controls the plug-in life cycle + * + * @author Kai Kreuzer - Initial contribution */ public class CoreActivator extends Plugin { diff --git a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/DesignerCoreConstants.java b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/DesignerCoreConstants.java index eb523afc771..e6d06615f24 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/DesignerCoreConstants.java +++ b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/DesignerCoreConstants.java @@ -7,6 +7,10 @@ */ package org.eclipse.smarthome.designer.core; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public interface DesignerCoreConstants { public static final String CONFIG_FOLDER_PREFERENCE = "CONFIG_FOLDER_PREFERENCE"; //$NON-NLS-1$ diff --git a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/ConfigurationFolderProvider.java b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/ConfigurationFolderProvider.java index 8152878357e..416711d0214 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/ConfigurationFolderProvider.java +++ b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/ConfigurationFolderProvider.java @@ -28,6 +28,10 @@ import org.osgi.service.prefs.Preferences; import org.slf4j.LoggerFactory; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class ConfigurationFolderProvider { private static IFolder folder; diff --git a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/GeneralProjectCreator.java b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/GeneralProjectCreator.java index 6b0b7fc046a..3788ec9eefe 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/GeneralProjectCreator.java +++ b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/GeneralProjectCreator.java @@ -12,6 +12,10 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +/** + * + * @author Oliver Libutzki - Initial contribution + */ public class GeneralProjectCreator implements IProjectCreator { @Override diff --git a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/IProjectCreator.java b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/IProjectCreator.java index 1450d05532f..437366400e1 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/IProjectCreator.java +++ b/bundles/designer/org.eclipse.smarthome.designer.core/src/main/java/org/eclipse/smarthome/designer/core/config/IProjectCreator.java @@ -9,6 +9,10 @@ import org.eclipse.core.resources.IProject; +/** + * + * @author Oliver Libutzki - Initial contribution + */ public interface IProjectCreator { IProject createProject(String projectName); } diff --git a/bundles/designer/org.eclipse.smarthome.designer.ui/src/main/java/org/eclipse/smarthome/designer/ui/UIActivator.java b/bundles/designer/org.eclipse.smarthome.designer.ui/src/main/java/org/eclipse/smarthome/designer/ui/UIActivator.java index 968b38bb8d6..f17b283836e 100644 --- a/bundles/designer/org.eclipse.smarthome.designer.ui/src/main/java/org/eclipse/smarthome/designer/ui/UIActivator.java +++ b/bundles/designer/org.eclipse.smarthome.designer.ui/src/main/java/org/eclipse/smarthome/designer/ui/UIActivator.java @@ -15,6 +15,8 @@ /** * The activator class controls the plug-in life cycle + * + * @author Kai Kreuzer - Initial contribution */ public class UIActivator extends AbstractUIPlugin { diff --git a/bundles/io/org.eclipse.smarthome.io.monitor/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.monitor/META-INF/MANIFEST.MF index 7708ee90eaa..65eb914adcf 100644 --- a/bundles/io/org.eclipse.smarthome.io.monitor/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.monitor/META-INF/MANIFEST.MF @@ -1,5 +1,5 @@ Manifest-Version: 1.0 -Service-Component: OSGI-INF/eventlogger.xml +Service-Component: OSGI-INF/*.xml Private-Package: org.eclipse.smarthome.core.monitor.internal Ignore-Package: org.eclipse.smarthome.core.monitor.internal Bundle-Name: Eclipse SmartHome Monitor diff --git a/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/EventLogger.java b/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/EventLogger.java index c52d5d4479a..8650e38964a 100644 --- a/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/EventLogger.java +++ b/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/EventLogger.java @@ -19,6 +19,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class EventLogger implements EventSubscriber { private final Map eventLoggers = Maps.newHashMap(); @@ -40,7 +44,7 @@ public void receive(Event event) { Logger logger = getLogger(event.getType()); logger.trace("Received event of type '{}' under the topic '{}' with payload: '{}'", event.getType(), event.getTopic(), event.getPayload()); - logger.info(event.toString()); + logger.info("{}", event); } private Logger getLogger(String eventType) { diff --git a/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/MonitorActivator.java b/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/MonitorActivator.java index 6e736ae651d..bb7992baf3c 100644 --- a/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/MonitorActivator.java +++ b/bundles/io/org.eclipse.smarthome.io.monitor/src/main/java/org/eclipse/smarthome/io/monitor/internal/MonitorActivator.java @@ -12,6 +12,8 @@ /** * Extension of the default OSGi bundle activator + * + * @author Kai Kreuzer - Intial contribution */ public final class MonitorActivator implements BundleActivator { diff --git a/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/exec/ExecUtil.java b/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/exec/ExecUtil.java index 5ab76c6e238..9856d3b8320 100644 --- a/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/exec/ExecUtil.java +++ b/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/exec/ExecUtil.java @@ -38,7 +38,7 @@ public class ExecUtil { * properly. In that cases another exec-method is to be used. To accomplish this please use the special delimiter ' * @@'. If commandLine contains this delimiter it is split into a String[] array and the * special exec-method is used. - * + * *

* A possible {@link IOException} gets logged but no further processing is done. * @@ -58,7 +58,7 @@ public static void executeCommandLine(String commandLine) { logger.info("executed commandLine '{}'", commandLine); } } catch (IOException e) { - logger.error("couldn't execute commandLine '" + commandLine + "'", e); + logger.error("couldn't execute commandLine '{}'", commandLine, e); } } @@ -68,7 +68,7 @@ public static void executeCommandLine(String commandLine) { * properly. In that cases another exec-method is to be used. To accomplish this please use the special delimiter ' * @@'. If commandLine contains this delimiter it is split into a String[] array and the * special exec-method is used. - * + * *

* A possible {@link IOException} gets logged but no further processing is done. * @@ -111,9 +111,9 @@ public static String executeCommandLineAndWaitResponse(String commandLine, int t executor.execute(cmdLine, resultHandler); logger.debug("executed commandLine '{}'", commandLine); } catch (ExecuteException e) { - logger.warn("couldn't execute commandLine '" + commandLine + "'", e); + logger.warn("couldn't execute commandLine '{}'", commandLine, e); } catch (IOException e) { - logger.warn("couldn't execute commandLine '" + commandLine + "'", e); + logger.warn("couldn't execute commandLine '{}'", commandLine, e); } // some time later the result handler callback was invoked so we @@ -123,12 +123,12 @@ public static String executeCommandLineAndWaitResponse(String commandLine, int t int exitCode = resultHandler.getExitValue(); retval = StringUtils.chomp(stdout.toString()); if (resultHandler.getException() != null) { - logger.warn(resultHandler.getException().getMessage()); + logger.warn("{}", resultHandler.getException().getMessage()); } else { logger.debug("exit code '{}', result '{}'", exitCode, retval); } } catch (InterruptedException e) { - logger.warn("Timeout occurred when executing commandLine '" + commandLine + "'", e); + logger.warn("Timeout occurred when executing commandLine '{}'", commandLine, e); } return retval; diff --git a/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/http/HttpUtil.java b/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/http/HttpUtil.java index 6f3171ea4c2..32b4c5a0b1b 100644 --- a/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/http/HttpUtil.java +++ b/bundles/io/org.eclipse.smarthome.io.net/src/main/java/org/eclipse/smarthome/io/net/http/HttpUtil.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.ProxyConfiguration; import org.eclipse.jetty.client.ProxyConfiguration.Proxy; +import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; @@ -193,18 +194,8 @@ private static ContentResponse executeUrlAndGetReponse(String httpMethod, String proxy = new HttpProxy(proxyHost, proxyPort); proxies.add(proxy); - // This value is a replacement for any realm - final String anyRealm = "*"; - authStore.addAuthentication(new BasicAuthentication(proxy.getURI(), anyRealm, proxyUser, proxyPassword) { - - // In version 9.2.12 Jetty HttpClient does not support adding an authentication for any realm. This is a - // workaround until this issue is solved - @Override - public boolean matches(String type, URI uri, String realm) { - realm = anyRealm; - return super.matches(type, uri, realm); - } - }); + authStore.addAuthentication( + new BasicAuthentication(proxy.getURI(), Authentication.ANY_REALM, proxyUser, proxyPassword)); } HttpMethod method = HttpUtil.createHttpMethod(httpMethod); diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core.test/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.rest.core.test/META-INF/MANIFEST.MF index d854d1ad2d0..457d0257033 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core.test/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.rest.core.test/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Import-Package: com.jayway.jsonpath, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.config.xml, diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF index 0d7d55764c4..9ab595ec7bc 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.rest.core/META-INF/MANIFEST.MF @@ -54,7 +54,9 @@ Import-Package: com.google.common.base, org.osgi.service.cm, org.osgi.service.component, org.slf4j -Export-Package: org.eclipse.smarthome.io.rest.core.item, +Export-Package: org.eclipse.smarthome.io.rest.core.config, + org.eclipse.smarthome.io.rest.core.service, + org.eclipse.smarthome.io.rest.core.item, org.eclipse.smarthome.io.rest.core.thing Service-Component: OSGI-INF/*.xml Bundle-Activator: org.eclipse.smarthome.io.rest.core.internal.RESTCoreActivator diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/binding/BindingResource.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/binding/BindingResource.java index c3194d87ebd..81065e2e319 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/binding/BindingResource.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/binding/BindingResource.java @@ -118,7 +118,7 @@ public Response getConfiguration( return configuration != null ? Response.ok(configuration.getProperties()).build() : Response.ok(Collections.emptyMap()).build(); } catch (IOException ex) { - logger.error("Cannot get configuration for service {}: " + ex.getMessage(), bindingId, ex); + logger.error("Cannot get configuration for service {}: {}", bindingId, ex.getMessage(), ex); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -147,7 +147,7 @@ public Response updateConfiguration( return oldConfiguration != null ? Response.ok(oldConfiguration.getProperties()).build() : Response.noContent().build(); } catch (IOException ex) { - logger.error("Cannot update configuration for service {}: " + ex.getMessage(), bindingId, ex); + logger.error("Cannot update configuration for service {}: {}", bindingId, ex.getMessage(), ex); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/service/ConfigurableServiceResource.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/service/ConfigurableServiceResource.java index 19946cc60ea..c05c6be6f24 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/service/ConfigurableServiceResource.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/service/ConfigurableServiceResource.java @@ -121,7 +121,7 @@ public Response getConfiguration( return configuration != null ? Response.ok(configuration.getProperties()).build() : Response.ok(Collections.emptyMap()).build(); } catch (IOException ex) { - logger.error("Cannot get configuration for service {}: " + ex.getMessage(), serviceId, ex); + logger.error("Cannot get configuration for service {}: {}", serviceId, ex.getMessage(), serviceId, ex); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -143,7 +143,7 @@ public Response updateConfiguration( return oldConfiguration != null ? Response.ok(oldConfiguration.getProperties()).build() : Response.noContent().build(); } catch (IOException ex) { - logger.error("Cannot update configuration for service {}: " + ex.getMessage(), serviceId, ex); + logger.error("Cannot update configuration for service {}: {}", serviceId, ex.getMessage(), ex); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -188,7 +188,7 @@ public Response deleteConfiguration( configurationService.delete(serviceId); return oldConfiguration != null ? Response.ok(oldConfiguration).build() : Response.noContent().build(); } catch (IOException ex) { - logger.error("Cannot delete configuration for service {}: " + ex.getMessage(), serviceId, ex); + logger.error("Cannot delete configuration for service {}: {}", serviceId, ex.getMessage(), ex); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -210,7 +210,7 @@ private List getConfigurableServices() { } } } catch (InvalidSyntaxException ex) { - logger.error("Cannot get service references, because syntax is invalid: " + ex.getMessage(), ex); + logger.error("Cannot get service references, because syntax is invalid: {}", ex.getMessage(), ex); } return services; } diff --git a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/thing/EnrichedThingDTOMapper.java b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/thing/EnrichedThingDTOMapper.java index 7ced8246d2b..ffd0fc8df5f 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/thing/EnrichedThingDTOMapper.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/thing/EnrichedThingDTOMapper.java @@ -23,6 +23,8 @@ /** * The {@link EnrichedThingDTOMapper} is an utility class to map things into enriched thing data transfer objects * (DTOs). + * + * @author Dennis Nobel - Initial contribution */ public class EnrichedThingDTOMapper extends ThingDTOMapper { diff --git a/bundles/io/org.eclipse.smarthome.io.rest.log/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.rest.log/META-INF/MANIFEST.MF index bedb0bc481e..9481b5fb96c 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.log/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.rest.log/META-INF/MANIFEST.MF @@ -10,4 +10,4 @@ Import-Package: io.swagger.annotations;resolution:=optional, javax.ws.rs.core, org.eclipse.smarthome.io.rest, org.slf4j -Service-Component: OSGI-INF/loghandler.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/io/org.eclipse.smarthome.io.rest.log/src/main/java/org/eclipse/smarthome/io/rest/log/internal/LogHandler.java b/bundles/io/org.eclipse.smarthome.io.rest.log/src/main/java/org/eclipse/smarthome/io/rest/log/internal/LogHandler.java index 4cb06af1e56..cd3ab68aa8a 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.log/src/main/java/org/eclipse/smarthome/io/rest/log/internal/LogHandler.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.log/src/main/java/org/eclipse/smarthome/io/rest/log/internal/LogHandler.java @@ -38,6 +38,10 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; +/** + * + * @author Sebastian Janzen - Initial contribution + */ @Path("/log") @Api(value = LogHandler.PATH_LOG) @Produces(MediaType.APPLICATION_JSON) diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/SitemapResource.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/SitemapResource.java index f521be78e35..d3f0c98c2af 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/SitemapResource.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/SitemapResource.java @@ -399,7 +399,7 @@ private WidgetDTO createWidgetBean(String sitemapName, Widget widget, boolean dr bean.item = EnrichedItemDTOMapper.map(item, false, UriBuilder.fromUri(uri).build(), locale); } } catch (ItemNotFoundException e) { - logger.debug(e.getMessage()); + logger.debug("{}", e.getMessage()); } } bean.widgetId = widgetId; diff --git a/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/internal/filter/ProxyFilter.java b/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/internal/filter/ProxyFilter.java index fe937b750c2..1fc11f7ea4a 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/internal/filter/ProxyFilter.java +++ b/bundles/io/org.eclipse.smarthome.io.rest/src/main/java/org/eclipse/smarthome/io/rest/internal/filter/ProxyFilter.java @@ -79,7 +79,7 @@ public void filter(ContainerRequestContext ctx) throws IOException { try { newBaseUri = new URI(uriString); } catch (URISyntaxException e) { - logger.error("Invalid X-Forwarded-Proto + X-Forwarded-Host header combination: " + uriString, e); + logger.error("Invalid X-Forwarded-Proto + X-Forwarded-Host header combination: {}", uriString, e); return; } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSActivator.java b/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSActivator.java index c66d9a25ce8..72383469488 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSActivator.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSActivator.java @@ -14,6 +14,8 @@ /** * Extension of the default OSGi bundle activator + * + * @author Kai Kreuzer - Initial contribution */ public final class MDNSActivator implements BundleActivator { diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSServiceImpl.java b/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSServiceImpl.java index 4dec82add08..4ebe86db9fe 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSServiceImpl.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mdns/src/main/java/org/eclipse/smarthome/io/transport/mdns/internal/MDNSServiceImpl.java @@ -52,7 +52,7 @@ public void run() { break; } } catch (IOException e) { - logger.error(e.getMessage()); + logger.error("{}", e.getMessage()); } catch (IllegalStateException e) { logger.debug("Not registering service {}, because service is already deactivated!", description.serviceType); @@ -83,7 +83,7 @@ public void run() { try { mdnsClient.registerService(description); } catch (IOException e) { - logger.error(e.getMessage()); + logger.error("{}", e.getMessage()); } catch (IllegalStateException e) { logger.debug("Not registering service {}, because service is already deactivated!", description.serviceType); diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnectionTests.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnectionTests.java new file mode 100644 index 00000000000..e83a785148a --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnectionTests.java @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; + +import javax.naming.ConfigurationException; + +import org.eclipse.paho.client.mqttv3.IMqttActionListener; +import org.eclipse.paho.client.mqttv3.IMqttToken; +import org.eclipse.smarthome.io.transport.mqtt.reconnect.AbstractReconnectStrategy; +import org.eclipse.smarthome.io.transport.mqtt.reconnect.PeriodicReconnectStrategy; +import org.junit.Test; + +/** + * Tests the MqttBrokerConnection class + * + * @author David Graeff - Initial contribution + */ +public class MqttBrokerConnectionTests { + @Test + public void testConstructor() throws ConfigurationException { + // Test tcp and ssl URLs + MqttBrokerConnection a = new MqttBrokerConnection("name", "tcp://123.123.123.123", false); + MqttBrokerConnection b = new MqttBrokerConnection("name", "ssl://123.123.123.123", true); + assertFalse(a.isTextualConfiguredBroker()); + assertTrue(b.isTextualConfiguredBroker()); + } + + @Test(expected = ConfigurationException.class) + public void testConstructorInvalidProtocol() throws ConfigurationException { + new MqttBrokerConnection("name", "unsupported://123.123.123.123", false); + } + + @Test(expected = ConfigurationException.class) + public void testConstructorInvalidName() throws ConfigurationException, MqttException { + new MqttBrokerConnection(" ", "tcp://123.123.123.123", false); + } + + @Test + public void messageConsumerTests() throws ConfigurationException, MqttException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); + // Expect no consumers + assertFalse(a.hasConsumers()); + + // Add a consumer (expect consumers getTopic() to be called) + MqttMessageSubscriber subscriber = mock(MqttMessageSubscriber.class); + when(subscriber.getTopic()).thenAnswer(i -> "topic"); + a.addConsumer(subscriber); + verify(subscriber).getTopic(); + assertTrue(a.hasConsumers()); + + // Remove consumer + a.removeConsumer(subscriber); + assertFalse(a.hasConsumers()); + } + + @Test + public void reconnectPolicyDefaultTest() throws ConfigurationException, MqttException, InterruptedException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); + + // Check if the default policy is set and that the broker within the policy is set. + assertTrue(a.getReconnectStrategy() instanceof PeriodicReconnectStrategy); + AbstractReconnectStrategy p = a.getReconnectStrategy(); + assertThat(p.getBrokerConnection(), is(a)); + } + + @Test + public void reconnectPolicyTests() throws ConfigurationException, MqttException, InterruptedException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = spy(new MqttBrokerConnection(name, url, false)); + + // Check setter + a.setReconnectStrategy(new PeriodicReconnectStrategy()); + assertThat(a.getReconnectStrategy().getBrokerConnection(), is(a)); + + // Prepare a Mock to test if lostConnect is called and + // if the PeriodicReconnectPolicy indeed calls start() + PeriodicReconnectStrategy mockPolicy = spy(new PeriodicReconnectStrategy()); + doReturn(a).when(mockPolicy).getBrokerConnection(); + doReturn(0).when(mockPolicy).getFirstReconnectAfter(); + doReturn(10000).when(mockPolicy).getReconnectFrequency(); + doNothing().when(a).start(); + + // Fake a disconnect + a.setReconnectStrategy(mockPolicy); + IMqttActionListener l = a.createConnectionListener(); + doReturn(false).when(a).isConnected(); + IMqttToken token = mock(IMqttToken.class); + when(token.getException()).thenReturn(new org.eclipse.paho.client.mqttv3.MqttException(1)); + l.onFailure(token, null); + + // Check lostConnect + verify(mockPolicy).lostConnection(); + Thread.sleep(10); + verify(a).start(); + assertTrue(mockPolicy.isReconnecting()); + + // Fake connection established + l.onSuccess(token); + assertFalse(mockPolicy.isReconnecting()); + } + + @Test + public void connectionObserverTests() throws ConfigurationException, MqttException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = spy(new MqttBrokerConnection(name, url, false)); + + // Add an observer + assertFalse(a.hasConnectionObservers()); + MqttConnectionObserver connectionObserver = mock(MqttConnectionObserver.class); + a.addConnectionObserver(connectionObserver); + assertTrue(a.hasConnectionObservers()); + + // Adding a connection observer should not immediately call its connectionStateChanged() method. + verify(connectionObserver, times(0)).connectionStateChanged(eq(MqttConnectionState.DISCONNECTED), anyObject()); + + // Cause a success callback + IMqttActionListener l = a.createConnectionListener(); + doReturn(true).when(a).isConnected(); + l.onSuccess(null); + verify(connectionObserver, times(1)).connectionStateChanged(eq(MqttConnectionState.CONNECTED), anyObject()); + + // Cause a failure callback with a mocked token + IMqttToken token = mock(IMqttToken.class); + org.eclipse.paho.client.mqttv3.MqttException testException = new org.eclipse.paho.client.mqttv3.MqttException( + 1); + when(token.getException()).thenReturn(testException); + + doReturn(false).when(a).isConnected(); + l.onFailure(token, null); + verify(connectionObserver, times(1)).connectionStateChanged(eq(MqttConnectionState.DISCONNECTED), + eq(testException)); + + // Remove observer + a.removeConnectionObserver(connectionObserver); + assertFalse(a.hasConnectionObservers()); + } + + @Test + public void lastWillAndTestamentTests() throws ConfigurationException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); + + assertNull(a.getLastWill()); + assertNull(MqttWillAndTestament.fromString("")); + a.setLastWill(MqttWillAndTestament.fromString("topic:message:1:true")); + assertTrue(a.getLastWill().getTopic().equals("topic")); + assertEquals(1, a.getLastWill().getQos()); + assertEquals(true, a.getLastWill().isRetain()); + byte b[] = { 'm', 'e', 's', 's', 'a', 'g', 'e' }; + assertTrue(Arrays.equals(a.getLastWill().getPayload(), b)); + } + + @Test(expected = IllegalArgumentException.class) + public void lastWillAndTestamentConstructorTests() { + new MqttWillAndTestament("", new byte[0], 0, false); + } + + @Test + public void setterGetterTests() throws ConfigurationException { + final String url = "tcp://123.123.123.123"; + final String name = "TestName12@!"; + MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); + assertEquals("URL getter", a.getUrl(), url); + assertEquals("Name getter", a.getName(), name); + + a.setClientId("clientid"); + assertEquals("ClientID getter/setter", "clientid", a.getClientId()); + // client ids longer than 23 characters should be ignored + a.setClientId("clientidclientidclientidclientid"); + assertEquals("ClientID too long check", "clientid", a.getClientId()); + + a.setCredentials("user@!", "password123@^"); + assertEquals("User getter/setter", "user@!", a.getUser()); + assertEquals("Password getter/setter", "password123@^", a.getPassword()); + + assertEquals(MqttBrokerConnection.DEFAULT_KEEPALIVE_INTERVAL, a.getKeepAliveInterval()); + a.setKeepAliveInterval(80); + assertEquals(80, a.getKeepAliveInterval()); + + assertFalse(a.isRetain()); + a.setRetain(true); + assertTrue(a.isRetain()); + + assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); + a.setQos(10); + assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); + a.setQos(-10); + assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); + a.setQos(2); + assertEquals(2, a.getQos()); + a.setQos(1); + assertEquals(1, a.getQos()); + + // Check for default ssl context provider and reconnect policy + assertNotNull(a.getSSLContextProvider()); + assertNotNull(a.getReconnectStrategy()); + + assertFalse(a.isConnected()); + } +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttServiceTests.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttServiceTests.java similarity index 87% rename from bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttServiceTests.java rename to bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttServiceTests.java index 4b2869e53fe..fd7825ef63d 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttServiceTests.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/MqttServiceTests.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.smarthome.io.transport.mqtt.test; +package org.eclipse.smarthome.io.transport.mqtt; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.*; @@ -17,10 +17,6 @@ import javax.naming.ConfigurationException; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokersObserver; -import org.eclipse.smarthome.io.transport.mqtt.MqttService; import org.junit.Test; /** @@ -33,11 +29,11 @@ public class MqttServiceTests { @Test public void brokerConnectionListenerTests() throws ConfigurationException { MqttService service = new MqttService(); - assertFalse(service.isBrokerObservers()); + assertFalse(service.hasBrokerObservers()); MqttBrokersObserver observer = mock(MqttBrokersObserver.class); service.addBrokersListener(observer); - assertTrue(service.isBrokerObservers()); + assertTrue(service.hasBrokerObservers()); MqttBrokerConnection connection = new MqttBrokerConnection("name", "tcp://123.123.123.123", false); assertTrue(service.addBrokerConnection(connection)); @@ -47,7 +43,7 @@ public void brokerConnectionListenerTests() throws ConfigurationException { verify(observer).brokerRemoved(connection); service.removeBrokersListener(observer); - assertFalse(service.isBrokerObservers()); + assertFalse(service.hasBrokerObservers()); } // Tests extractBrokerConfigurations() and addBrokerConnection(map) diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttBrokerConnectionTests.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttBrokerConnectionTests.java deleted file mode 100644 index dd7075725c8..00000000000 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt.test/src/test/java/org/eclipse/smarthome/io/transport/mqtt/test/MqttBrokerConnectionTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt.test; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; - -import javax.naming.ConfigurationException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.eclipse.smarthome.io.transport.mqtt.MqttConnectionObserver; -import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber; -import org.eclipse.smarthome.io.transport.mqtt.MqttWillAndTestament; -import org.junit.Test; - -/** - * Tests the MqttBrokerConnection class - * - * @author David Graeff - Initial contribution - */ -public class MqttBrokerConnectionTests { - @Test - public void testConstructor() throws ConfigurationException { - // Test tcp and ssl URLs - MqttBrokerConnection a = new MqttBrokerConnection("name", "tcp://123.123.123.123", false); - MqttBrokerConnection b = new MqttBrokerConnection("name", "ssl://123.123.123.123", true); - assertFalse(a.isTextualConfiguredBroker()); - assertTrue(b.isTextualConfiguredBroker()); - } - - @Test(expected = ConfigurationException.class) - public void testConstructorInvalidProtocol() throws ConfigurationException { - new MqttBrokerConnection("name", "unsupported://123.123.123.123", false); - } - - @Test(expected = ConfigurationException.class) - public void testConstructorInvalidName() throws ConfigurationException, MqttException { - new MqttBrokerConnection(" ", "tcp://123.123.123.123", false); - } - - @Test - public void messageConsumerTests() throws ConfigurationException, MqttException { - final String url = "tcp://123.123.123.123"; - final String name = "TestName12@!"; - MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); - assertFalse(a.isConsumers()); - MqttMessageSubscriber subscriber = mock(MqttMessageSubscriber.class); - when(subscriber.getTopic()).thenAnswer(i -> "topic"); - a.addConsumer(subscriber); - assertTrue(a.isConsumers()); - a.removeConsumer(subscriber); - assertFalse(a.isConsumers()); - } - - @Test - public void connectionObserverTests() throws ConfigurationException { - final String url = "tcp://123.123.123.123"; - final String name = "TestName12@!"; - MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); - assertFalse(a.isConnectionObservers()); - MqttConnectionObserver connectionObserver = mock(MqttConnectionObserver.class); - a.addConnectionObserver(connectionObserver); - // Adding a connection observer should immediately call its setConnected() method. - verify(connectionObserver).setConnected(false); - - assertTrue(a.isConnectionObservers()); - a.removeConnectionObserver(connectionObserver); - assertFalse(a.isConnectionObservers()); - } - - @Test - public void setterGetterTests() throws ConfigurationException { - final String url = "tcp://123.123.123.123"; - final String name = "TestName12@!"; - MqttBrokerConnection a = new MqttBrokerConnection(name, url, false); - assertEquals("URL getter", a.getUrl(), url); - assertEquals("Name getter", a.getName(), name); - - a.setClientId("clientid"); - assertEquals("ClientID getter/setter", "clientid", a.getClientId()); - // client ids longer than 23 characters should be ignored - a.setClientId("clientidclientidclientidclientid"); - assertEquals("ClientID too long check", "clientid", a.getClientId()); - - a.setCredentials("user@!", "password123@^"); - assertEquals("User getter/setter", "user@!", a.getUser()); - assertEquals("Password getter/setter", "password123@^", a.getPassword()); - - assertEquals(MqttBrokerConnection.DEFAULT_KEEPALIVE_INTERVAL, a.getKeepAliveInterval()); - a.setKeepAliveInterval(80); - assertEquals(80, a.getKeepAliveInterval()); - - assertFalse(a.isRetain()); - a.setRetain(true); - assertTrue(a.isRetain()); - - assertNull(a.getLastWill()); - assertNull(MqttWillAndTestament.fromString("")); - a.setLastWill(MqttWillAndTestament.fromString("topic:message:1:true")); - assertTrue(a.getLastWill().getTopic().equals("topic")); - assertEquals(1, a.getLastWill().getQos()); - assertEquals(true, a.getLastWill().isRetain()); - byte b[] = { 'm', 'e', 's', 's', 'a', 'g', 'e' }; - assertTrue(Arrays.equals(a.getLastWill().getPayload(), b)); - - assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); - a.setQos(10); - assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); - a.setQos(-10); - assertEquals(MqttBrokerConnection.DEFAULT_QOS, a.getQos()); - a.setQos(2); - assertEquals(2, a.getQos()); - a.setQos(1); - assertEquals(1, a.getQos()); - - // Check for default ssl context provider and reconnect policy - assertNotNull(a.getSSLContextProvider()); - assertNotNull(a.getReconnectPolicy()); - - assertFalse(a.isConnected()); - } -} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/META-INF/MANIFEST.MF index a5817205b1e..5b83e9782a6 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/META-INF/MANIFEST.MF @@ -5,7 +5,9 @@ Bundle-SymbolicName: org.eclipse.smarthome.io.transport.mqtt;singleton:=true Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.eclipse.smarthome.io.transport.mqtt +Export-Package: org.eclipse.smarthome.io.transport.mqtt, + org.eclipse.smarthome.io.transport.mqtt.reconnect, + org.eclipse.smarthome.io.transport.mqtt.sslcontext Import-Package: javax.naming, javax.net, javax.net.ssl, @@ -18,6 +20,6 @@ Import-Package: javax.naming, org.eclipse.smarthome.io.transport.mqtt, org.osgi.service.cm, org.slf4j -Service-Component: OSGI-INF/mqtt-*.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . Bundle-ActivationPolicy: lazy diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnection.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnection.java index 6a7794537d1..ff34e6d9bc0 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnection.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttBrokerConnection.java @@ -23,15 +23,13 @@ import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; import org.eclipse.smarthome.io.transport.mqtt.internal.MqttSenderChannelImpl; -import org.eclipse.smarthome.io.transport.mqtt.reconnect.AbstractReconnectPolicy; -import org.eclipse.smarthome.io.transport.mqtt.reconnect.PeriodicReconnectPolicy; -import org.eclipse.smarthome.io.transport.sslcontext.AcceptAllCertifcatesSSLContext; -import org.eclipse.smarthome.io.transport.sslcontext.SSLContextProvider; +import org.eclipse.smarthome.io.transport.mqtt.reconnect.AbstractReconnectStrategy; +import org.eclipse.smarthome.io.transport.mqtt.reconnect.PeriodicReconnectStrategy; +import org.eclipse.smarthome.io.transport.mqtt.sslcontext.AcceptAllCertificatesSSLContext; +import org.eclipse.smarthome.io.transport.mqtt.sslcontext.SSLContextProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +39,7 @@ * * When a connection to an MQTT broker is lost, it will try to reconnect every 60 seconds. * - * @author David Graeff - All operations are async now. More flexible sslContextProvider and reconnectPolicy added. + * @author David Graeff - All operations are async now. More flexible sslContextProvider and reconnectStrategy added. * @author Davy Vanherbergen * @author Markus Rathgeb - added connection state callback */ @@ -60,8 +58,8 @@ public class MqttBrokerConnection { private int qos = DEFAULT_QOS; private boolean retain = false; private MqttWillAndTestament lastWill; - private AbstractReconnectPolicy reconnectPolicy = new PeriodicReconnectPolicy(); - private SSLContextProvider sslContextProvider = new AcceptAllCertifcatesSSLContext(); + private AbstractReconnectStrategy reconnectStrategy; + private SSLContextProvider sslContextProvider = new AcceptAllCertificatesSSLContext(); private int keepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; /// Runtime variables @@ -93,10 +91,10 @@ public synchronized void connectionLost(Throwable t) { } for (final MqttConnectionObserver connectionObserver : connectionObservers) { - connectionObserver.setConnected(false); + connectionObserver.connectionStateChanged(MqttConnectionState.DISCONNECTED, t); } - reconnectPolicy.lostConnection(); + reconnectStrategy.lostConnection(); } @Override @@ -146,20 +144,22 @@ public MqttBrokerConnection(String name, String url, boolean textualConfiguredBr throw new ConfigurationException( "No valid url for the broker set! Must be tcp://localhost:1234 or ssl://localhost:1234. Port is optional."); } + setReconnectStrategy(new PeriodicReconnectStrategy()); } /** - * Set the reconnect policy. The implementor will be called when the connection + * Set the reconnect strategy. The implementor will be called when the connection * to the Mqtt broker is lost and also when it is established. * - * @param reconnectPolicy The reconnect policy. May not be null. + * @param reconnectStrategy The reconnect strategy. May not be null. */ - public void setReconnectPolicy(AbstractReconnectPolicy reconnectPolicy) { - this.reconnectPolicy = reconnectPolicy; + public void setReconnectStrategy(AbstractReconnectStrategy reconnectStrategy) { + this.reconnectStrategy = reconnectStrategy; + reconnectStrategy.setBrokerConnection(this); } - public AbstractReconnectPolicy getReconnectPolicy() { - return this.reconnectPolicy; + public AbstractReconnectStrategy getReconnectStrategy() { + return this.reconnectStrategy; } /** @@ -332,7 +332,7 @@ public void setSSLContextProvider(SSLContextProvider sslContextProvider) { /** * Return true if there are consumers registered via addConsumer(). */ - public boolean isConsumers() { + public boolean hasConsumers() { return !consumers.isEmpty(); } @@ -357,7 +357,11 @@ public boolean addConsumer(MqttMessageSubscriber subscriber) throws MqttExceptio consumers.put(topic, subscriberList); } if (isConnected()) { - client.subscribe(topic, qos); + try { + client.subscribe(topic, qos); + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { + throw new MqttException(e); + } } return true; } @@ -375,7 +379,7 @@ public void removeConsumer(MqttMessageSubscriber subscriber) { if (isConnected()) { client.unsubscribe(subscriber.getTopic()); } - } catch (MqttException e) { + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { logger.info("Error unsubscribing topic from broker", e); } @@ -397,7 +401,6 @@ public void removeConsumer(MqttMessageSubscriber subscriber) { */ public synchronized void addConnectionObserver(MqttConnectionObserver connectionObserver) { connectionObservers.add(connectionObserver); - connectionObserver.setConnected(isConnected()); } /** @@ -412,7 +415,7 @@ public synchronized void removeConnectionObserver(MqttConnectionObserver connect /** * Return true if there are connection observers registered via addConnectionObserver(). */ - public boolean isConnectionObservers() { + public boolean hasConnectionObservers() { return !connectionObservers.isEmpty(); } @@ -444,30 +447,10 @@ public synchronized void removeProducer(MqttMessageProducer publisher) { } /** - * This will establish a connection to the MQTT broker and if successful, notify all - * publishers and subscribers that the connection has become active. This method will - * do nothing if there is already an active connection. - * - * @throws MqttException If a communication error occurred, this exception is thrown. - * @throws ConfigurationException If no url is given or parameters are invalid, this exception is thrown. + * Create a MqttConnectOptions object using the fields of this MqttBrokerConnection instance. + * Package local, for testing. */ - public synchronized void start() throws MqttException, ConfigurationException { - if (isConnecting || isConnected()) { - return; - } - - if (StringUtils.isBlank(clientId) || clientId.length() > 23) { - clientId = MqttClient.generateClientId(); - } - - String tmpDir = System.getProperty("java.io.tmpdir") + "/" + getName(); - MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); - logger.debug("Creating new client for '{}' using id '{}' and file store '{}'", getUrl(), clientId, tmpDir); - client = new MqttAsyncClient(getUrl(), clientId, dataStore); - client.setCallback(clientCallbacks); - - logger.info("Starting MQTT broker connection '{}' with clientid {}", getName(), getClientId()); - + MqttConnectOptions createMqttOptions() throws ConfigurationException { MqttConnectOptions options = new MqttConnectOptions(); if (!StringUtils.isBlank(user)) { @@ -477,7 +460,7 @@ public synchronized void start() throws MqttException, ConfigurationException { options.setPassword(password.toCharArray()); } if (getUrl().toLowerCase().startsWith("ssl")) { - options.setSocketFactory(this.sslContextProvider.getContext().getSocketFactory()); + options.setSocketFactory(sslContextProvider.getContext().getSocketFactory()); } if (lastWill != null) { @@ -485,21 +468,29 @@ public synchronized void start() throws MqttException, ConfigurationException { } options.setKeepAliveInterval(keepAliveInterval); + return options; + } - isConnecting = true; - client.connect(options, null, new IMqttActionListener() { + /** + * Create a IMqttActionListener object for being used as a callback for a connection attempt. + * The callback will interact with the {@link AbstractReconnectStrategy} as well as inform registered + * {@link MqttConnectionObserver}s. + * Package local, for testing. + */ + IMqttActionListener createConnectionListener() { + return new IMqttActionListener() { @Override - public void onSuccess(IMqttToken arg0) { + public void onSuccess(IMqttToken asyncActionToken) { isConnecting = false; - reconnectPolicy.connectionEstablished(); + reconnectStrategy.connectionEstablished(); // start all consumers for (List consumerList : consumers.values()) { for (MqttMessageSubscriber c : consumerList) { try { client.subscribe(c.getTopic(), qos); - } catch (MqttException e) { + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { logger.debug("Couldn't start subscriber", e); } } @@ -511,16 +502,73 @@ public void onSuccess(IMqttToken arg0) { } for (final MqttConnectionObserver connectionObserver : connectionObservers) { - connectionObserver.setConnected(isConnected()); + connectionObserver.connectionStateChanged( + isConnected() ? MqttConnectionState.CONNECTED : MqttConnectionState.DISCONNECTED, null); } } @Override - public void onFailure(IMqttToken arg0, Throwable arg1) { + public void onFailure(IMqttToken asyncActionToken, Throwable exception) { isConnecting = false; - reconnectPolicy.lostConnection(); + for (final MqttConnectionObserver connectionObserver : connectionObservers) { + connectionObserver.connectionStateChanged( + isConnected() ? MqttConnectionState.CONNECTED : MqttConnectionState.DISCONNECTED, + asyncActionToken.getException()); + } + reconnectStrategy.lostConnection(); } - }); + }; + } + + /** + * This will establish a connection to the MQTT broker and if successful, notify all + * publishers and subscribers that the connection has become active. This method will + * do nothing if there is already an active connection. + * + * If you want a synchronised way of establishing a broker connection, you can use the + * following pattern: + * + * Object o = new Object(); + * conn.addConnectionObserver((isConnected, error) -> o.notify() ); + * conn.start(); + * o.wait(timeout_in_ms); + * boolean success = conn.isConnected(); + * + * @throws MqttException If a communication error occurred, this exception is thrown. + * @throws ConfigurationException If no url is given or parameters are invalid, this exception is thrown. + */ + public synchronized void start() throws MqttException, ConfigurationException { + if (isConnecting || isConnected()) { + return; + } + + if (StringUtils.isBlank(clientId) || clientId.length() > 23) { + clientId = MqttClient.generateClientId(); + } + + // Storage + String tmpDir = System.getProperty("java.io.tmpdir") + "/" + getName(); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); + + // Create client + logger.debug("Creating new client for '{}' using id '{}' and file store '{}'", getUrl(), clientId, tmpDir); + try { + client = new MqttAsyncClient(getUrl(), clientId, dataStore); + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { + throw new MqttException(e); + } + client.setCallback(clientCallbacks); + + logger.info("Starting MQTT broker connection '{}' with clientid {}", getName(), getClientId()); + + // Perform the connection attempt + isConnecting = true; + + try { + client.connect(createMqttOptions(), null, createConnectionListener()); + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { + throw new MqttException(e); + } } /** @@ -533,11 +581,12 @@ public synchronized void close() { client.disconnect(); client = null; } - } catch (MqttException e) { + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { logger.info("Error closing connection to broker", e); } + for (final MqttConnectionObserver connectionObserver : connectionObservers) { - connectionObserver.setConnected(false); + connectionObserver.connectionStateChanged(MqttConnectionState.DISCONNECTED, null); } } @@ -547,14 +596,28 @@ public synchronized void close() { * @param topic The topic * @param payload The message payload * @param listener An optional listener to be notified of success or failure of the delivery. - * @throws MqttPersistenceException + * @return The message ID of the published message. Can be used in the callback to identify the asynchronous task. * @throws MqttException */ - public void publish(String topic, byte[] payload, IMqttActionListener listener) - throws MqttPersistenceException, MqttException { + public int publish(String topic, byte[] payload, MqttPublishCallback listener) throws MqttException { // publish message asynchronously IMqttDeliveryToken deliveryToken; - deliveryToken = client.publish(topic, payload, qos, retain, null, listener); + try { + deliveryToken = client.publish(topic, payload, qos, retain, null, new IMqttActionListener() { + @Override + public void onSuccess(IMqttToken token) { + listener.onSuccess(new MqttPublishResult(token.getMessageId(), topic)); + } + + @Override + public void onFailure(IMqttToken token, Throwable error) { + listener.onFailure(new MqttPublishResult(token.getMessageId(), topic), error); + } + }); + } catch (org.eclipse.paho.client.mqttv3.MqttException e) { + throw new MqttException(e); + } logger.debug("Publishing message {} to topic '{}'", deliveryToken.getMessageId(), topic); + return deliveryToken.getMessageId(); } } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionObserver.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionObserver.java index dc4cd61b4cf..6fb4fd5c565 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionObserver.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionObserver.java @@ -11,14 +11,18 @@ * Implement this interface to get notified of connection state changes. * Register this observer at {@see MqttBrokerConnection}. * + * @author David Graeff - Rewritten * @author Markus Rathgeb - Initial contribution and API */ public interface MqttConnectionObserver { /** - * Inform the observer if the connection is active or disconnected. + * Inform the observer if a connection could be established or if a connection + * is lost. This will be issued in the context of the Mqtt client thread and + * requires that the control is returned quickly to not stall the Mqtt thread. * - * @param connected true if the connection is established, false if disconnected. + * @param state The new connection state + * @param error An exception object (might be a MqttException) with the reason why + * a connection failed. */ - public void setConnected(boolean connected); - + public void connectionStateChanged(MqttConnectionState state, Throwable error); } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/SSLContextProvider.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionState.java similarity index 58% rename from bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/SSLContextProvider.java rename to bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionState.java index b135c3dc6eb..0683819c3d4 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/SSLContextProvider.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttConnectionState.java @@ -5,11 +5,14 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.smarthome.io.transport.sslcontext; +package org.eclipse.smarthome.io.transport.mqtt; -import javax.naming.ConfigurationException; -import javax.net.ssl.SSLContext; - -public interface SSLContextProvider { - SSLContext getContext() throws ConfigurationException; +/** + * The connection state of a {@link MqttBrokerConnection}. + * + * @author David Graeff + */ +public enum MqttConnectionState { + DISCONNECTED, + CONNECTED } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttException.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttException.java new file mode 100644 index 00000000000..fdc75ed3ac9 --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttException.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt; + +/** + * Thrown if an error occurs communicating with the server. + * The exception contains a reason code. The semantic of the reason + * code depends on the underlying implementation. + * + * @author David Graeff - Initial contribution + */ +public class MqttException extends Exception { + private static final long serialVersionUID = 301L; + private int reasonCode; + private Throwable cause; + + /** + * Constructs a new MqttException with the specified code + * as the underlying reason. + * + * @param reasonCode the reason code for the exception. + */ + public MqttException(int reasonCode) { + this.reasonCode = reasonCode; + } + + /** + * Constructs a new MqttException with the specified + * Throwable as the underlying reason. + * + * @param cause the underlying cause of the exception. + */ + public MqttException(Throwable cause) { + if (cause instanceof org.eclipse.paho.client.mqttv3.MqttException) { + org.eclipse.paho.client.mqttv3.MqttException internalException = (org.eclipse.paho.client.mqttv3.MqttException) cause; + this.reasonCode = internalException.getReasonCode(); + } else { + this.reasonCode = org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_CLIENT_EXCEPTION; + } + this.cause = cause; + } + + /** + * Constructs a new MqttException with the specified + * Throwable as the underlying reason. + * + * @param reason the reason code for the exception. + * @param cause the underlying cause of the exception. + */ + public MqttException(int reason, Throwable cause) { + this.reasonCode = reason; + this.cause = cause; + } + + /** + * Returns the reason code for this exception. + * + * @return the code representing the reason for this exception. + */ + public int getReasonCode() { + return reasonCode; + } + + /** + * Returns the underlying cause of this exception, if available. + * + * @return the Throwable that was the root cause of this exception, + * which may be null. + */ + @Override + public Throwable getCause() { + return cause; + } + + /** + * Returns the detail message for this exception. May be null. + */ + @Override + public String getMessage() { + if (cause != null) { + return cause.getMessage(); + } + + return "MqttException with reason " + String.valueOf(reasonCode); + } + + /** + * Returns a String representation of this exception. + */ + @Override + public String toString() { + if (cause instanceof org.eclipse.paho.client.mqttv3.MqttException) { + org.eclipse.paho.client.mqttv3.MqttException internalException = (org.eclipse.paho.client.mqttv3.MqttException) cause; + return internalException.toString(); + } + String result = getMessage() + " (" + reasonCode + ")"; + if (cause != null) { + result = result + " - " + cause.toString(); + } + return result; + } +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishCallback.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishCallback.java new file mode 100644 index 00000000000..d5022bc3302 --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishCallback.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt; + +/** + * Implement this to be notified of the success or error of a {@link MqttBrokerConnection}.publish(). + * + * @author David Graeff - Initial contribution + */ +public interface MqttPublishCallback { + public void onSuccess(MqttPublishResult result); + + public void onFailure(MqttPublishResult result, Throwable error); +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishResult.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishResult.java new file mode 100644 index 00000000000..3c5acdd0e50 --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttPublishResult.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt; + +/** + * Represents a publish result asynchronously provided by the {@link MqttPublishCallback} + * after a call to {@link MqttBrokerConnection}.publish(). + * + * @author David Graeff - Initial contribution + */ +public class MqttPublishResult { + final int messageID; + String topic; + + /** + * Package local and only to be used by {@link MqttBrokerConnection}.publish() and tests. + * + * @param messageID + * @param topic + */ + MqttPublishResult(int messageID, String topic) { + this.messageID = messageID; + this.topic = topic; + } + + /** + * Return the topic, that the publish was targeted on. + */ + public String getTopic() { + return topic; + } + + /** + * Return the messageID that was used to send the message to the broker. + */ + public int getMessageID() { + return messageID; + } +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java index 7f9c00bc98f..508d1ea15b0 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java @@ -20,7 +20,6 @@ import javax.naming.ConfigurationException; import org.apache.commons.lang.StringUtils; -import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.smarthome.core.events.EventPublisher; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; @@ -150,7 +149,7 @@ public void removeBrokersListener(MqttBrokersObserver observer) { /** * Return true if a broker listener has been added via addBrokersListener(). */ - public boolean isBrokerObservers() { + public boolean hasBrokerObservers() { return !brokersObservers.isEmpty(); } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttWillAndTestament.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttWillAndTestament.java index 6119bd61c03..6c9960c21ee 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttWillAndTestament.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttWillAndTestament.java @@ -66,6 +66,31 @@ public static MqttWillAndTestament fromString(String string) { return result.isValid() ? result : null; } + /** + * Hide the constructor and force consumers to use the fromString() method or the + * constructor requiring all field parameters to be set. + */ + private MqttWillAndTestament() { + } + + /** + * Create a new {@link} MqttWillAndTestament with at least a topic name. + * + * @param topic topic is a normal topic string (no placeholders are allowed) + * @param payload The optional payload. Can be null. + * @param qos Valid values are 0 (Deliver at most once),1 (Deliver at least once) or 2 + * @param retain true if messages shall be retained + */ + public MqttWillAndTestament(String topic, byte[] payload, int qos, boolean retain) { + if (StringUtils.isBlank(topic)) { + throw new IllegalArgumentException("Topic must be set"); + } + this.topic = topic; + this.payload = payload; + this.qos = qos; + this.retain = retain; + } + /** * Return true if the last will and testament object is valid. */ diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/internal/MqttSenderChannelImpl.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/internal/MqttSenderChannelImpl.java index 8c792b0cda6..07034ccb4c2 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/internal/MqttSenderChannelImpl.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/internal/MqttSenderChannelImpl.java @@ -9,12 +9,16 @@ import java.io.IOException; -import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.eclipse.smarthome.io.transport.mqtt.MqttException; import org.eclipse.smarthome.io.transport.mqtt.MqttSenderChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * @author David Gräff - Initial contribution + */ @Deprecated public class MqttSenderChannelImpl implements MqttSenderChannel { private final MqttBrokerConnection connection; diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectPolicy.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectPolicy.java deleted file mode 100644 index 7c8fa0cbe12..00000000000 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectPolicy.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt.reconnect; - -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; - -/** - * Implement this class and be notified of a lost broker connection. - * - * @author David Graeff - */ -public abstract class AbstractReconnectPolicy { - protected MqttBrokerConnection brokerConnection; - - /** - * Will be called by {@see MqttBrokerConnection.setReconnectPolicy()}. - * - * @param brokerConnection The broker connection - */ - void setBrokerConnection(MqttBrokerConnection brokerConnection) { - this.brokerConnection = brokerConnection; - } - - public abstract void lostConnection(); - - public abstract void connectionEstablished(); -} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectStrategy.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectStrategy.java new file mode 100644 index 00000000000..edb78348313 --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/AbstractReconnectStrategy.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt.reconnect; + +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.eclipse.smarthome.io.transport.mqtt.MqttConnectionObserver; + +/** + * Implement this class to provide a strategy for (re)establishing a lost + * broker connection. + * + * @author David Graeff - Initial contribution + */ +public abstract class AbstractReconnectStrategy { + protected MqttBrokerConnection brokerConnection; + + /** + * Will be called by {@see MqttBrokerConnection.setReconnectPolicy()}. + * + * @param brokerConnection The broker connection + */ + public void setBrokerConnection(MqttBrokerConnection brokerConnection) { + this.brokerConnection = brokerConnection; + } + + /** + * Return the brokerConnection object that this reconnect policy is assigned to. + */ + public MqttBrokerConnection getBrokerConnection() { + return brokerConnection; + } + + /** + * Return true if your implementation is trying to establish a connection, false otherwise. + */ + public abstract boolean isReconnecting(); + + /** + * The {@link MqttConnectionObserver} will call this method if a broker connection has been lost + * or couldn't be established. Your implementation should start trying to reestablish a connection. + */ + public abstract void lostConnection(); + + /** + * The {@link MqttConnectionObserver} will call this method if a broker connection has been + * successfully established. Your implementation should stop reconnection attempts and release + * resources. + */ + public abstract void connectionEstablished(); +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectPolicy.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectStrategy.java similarity index 60% rename from bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectPolicy.java rename to bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectStrategy.java index be7ae87324f..129ab9859cc 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectPolicy.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/reconnect/PeriodicReconnectStrategy.java @@ -14,25 +14,51 @@ import javax.naming.ConfigurationException; -import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.smarthome.io.transport.mqtt.MqttException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PeriodicReconnectPolicy extends AbstractReconnectPolicy { - private final Logger logger = LoggerFactory.getLogger(PeriodicReconnectPolicy.class); +/** + * This is an implementation of the {@link AbstractReconnectStrategy}. This + * strategy tries to reconnect after 10 seconds and then every 60 seconds + * after a broker connection has been lost. + * + * @author David Graeff - Initial contribution + */ +public class PeriodicReconnectStrategy extends AbstractReconnectStrategy { + private final Logger logger = LoggerFactory.getLogger(PeriodicReconnectStrategy.class); private int reconnectFrequency = 60000; private int firstReconnectAfter = 10000; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private ScheduledFuture scheduledTask; + /** + * Use a default 60s reconnect frequency and try the first reconnect after 10s. + */ + public PeriodicReconnectStrategy() { + } + + /** + * Create a {@link PeriodicReconnectStrategy} with the given reconnect frequency and + * first reconnect time parameters. + * + * @param reconnectFrequency This strategy tries to reconnect in this frequency in ms. + * @param firstReconnectAfter After a connection is lost, the very first reconnect attempt will be performed after + * this time in ms. + */ + public PeriodicReconnectStrategy(int reconnectFrequency, int firstReconnectAfter) { + this.reconnectFrequency = reconnectFrequency; + this.firstReconnectAfter = firstReconnectAfter; + } + @Override public void lostConnection() { logger.info("Starting connection helper to periodically try restore connection to broker '{}'", - brokerConnection.getName()); + getBrokerConnection().getName()); this.scheduledTask = scheduler.scheduleWithFixedDelay(() -> { try { - brokerConnection.start(); + getBrokerConnection().start(); } catch (MqttException | ConfigurationException e) { logger.warn("Broker connection couldn't be started", e); } @@ -47,6 +73,11 @@ public void connectionEstablished() { } } + @Override + public boolean isReconnecting() { + return scheduledTask != null; + } + public int getReconnectFrequency() { return reconnectFrequency; } diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/AcceptAllCertifcatesSSLContext.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/AcceptAllCertificatesSSLContext.java similarity index 52% rename from bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/AcceptAllCertifcatesSSLContext.java rename to bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/AcceptAllCertificatesSSLContext.java index 03d20ad0b4f..afefa0a9a9f 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/sslcontext/AcceptAllCertifcatesSSLContext.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/AcceptAllCertificatesSSLContext.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.smarthome.io.transport.sslcontext; +package org.eclipse.smarthome.io.transport.mqtt.sslcontext; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -16,33 +16,38 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import org.eclipse.smarthome.io.transport.mqtt.reconnect.PeriodicReconnectPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AcceptAllCertifcatesSSLContext implements SSLContextProvider { - private final Logger logger = LoggerFactory.getLogger(PeriodicReconnectPolicy.class); +/** + * This SSLContextProvider returns an {@link SSLContext} that accepts all connections and doesn't perform any + * certificate validations. This implementation forces a TLS v1.2 {@link SSLContext} instance. + * + * @author David Graeff - Initial contribution + */ +public class AcceptAllCertificatesSSLContext implements SSLContextProvider { + private final Logger logger = LoggerFactory.getLogger(AcceptAllCertificatesSSLContext.class); + + TrustManager trustManager = new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }; @Override public SSLContext getContext() throws ConfigurationException { - // use standard JSSE available in the runtime and - // use TLSv1.2 which is the default for a secured mosquitto try { SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new TrustManager[] { new X509TrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - @Override - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - @Override - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } }, new java.security.SecureRandom()); + sslContext.init(null, new TrustManager[] { trustManager }, null); return sslContext; } catch (KeyManagementException | NoSuchAlgorithmException e) { logger.warn("SSL configuration failed", e); diff --git a/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/SSLContextProvider.java b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/SSLContextProvider.java new file mode 100644 index 00000000000..d147dabb555 --- /dev/null +++ b/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/sslcontext/SSLContextProvider.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.io.transport.mqtt.sslcontext; + +import javax.naming.ConfigurationException; +import javax.net.ssl.SSLContext; + +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; + +/** + * Implement this and provide a {@link SSLContext} instance to be used by the {@link MqttBrokerConnection} for secure + * Mqtt broker connections where the URL starts with 'ssl://'. Register your implementation with + * {@link MqttBrokerConnection.setSSLContextProvider}. + * + * @author David Graeff - Initial contribution + */ +public interface SSLContextProvider { + /** + * Return an {@link SSLContext} to be used by secure Mqtt broker connections. Never return null here. If you are not + * able to create an {@link SSLContext} instance, fail with a ConfigurationException instead. + */ + SSLContext getContext() throws ConfigurationException; +} diff --git a/bundles/io/org.eclipse.smarthome.io.transport.upnp/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.transport.upnp/META-INF/MANIFEST.MF index f925607e17e..73166fb9320 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.upnp/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.transport.upnp/META-INF/MANIFEST.MF @@ -18,5 +18,5 @@ Import-Package: org.eclipse.smarthome.io.transport.upnp, org.osgi.framework;version="1.3.0", org.slf4j Export-Package: org.eclipse.smarthome.io.transport.upnp -Service-Component: OSGI-INF/UpnpIOService.xml +Service-Component: OSGI-INF/*.xml Bundle-Vendor: Eclipse.org/SmartHome diff --git a/bundles/io/org.eclipse.smarthome.io.transport.upnp/build.properties b/bundles/io/org.eclipse.smarthome.io.transport.upnp/build.properties index 39e058ee400..d045ac4502d 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.upnp/build.properties +++ b/bundles/io/org.eclipse.smarthome.io.transport.upnp/build.properties @@ -2,4 +2,5 @@ source.. = src/main/java output.. = target/classes bin.includes = META-INF/,\ .,\ - OSGI-INF/ + OSGI-INF/,\ + about.html diff --git a/bundles/io/org.eclipse.smarthome.io.transport.upnp/src/main/java/org/eclipse/smarthome/io/transport/upnp/UpnpIOServiceImpl.java b/bundles/io/org.eclipse.smarthome.io.transport.upnp/src/main/java/org/eclipse/smarthome/io/transport/upnp/UpnpIOServiceImpl.java index 3066dc6889b..844333291a1 100644 --- a/bundles/io/org.eclipse.smarthome.io.transport.upnp/src/main/java/org/eclipse/smarthome/io/transport/upnp/UpnpIOServiceImpl.java +++ b/bundles/io/org.eclipse.smarthome.io.transport.upnp/src/main/java/org/eclipse/smarthome/io/transport/upnp/UpnpIOServiceImpl.java @@ -303,7 +303,7 @@ public Map invokeAction(UpnpIOParticipant participant, String se ActionException anException = invocation.getFailure(); if (anException != null && anException.getMessage() != null) { - logger.debug(anException.getMessage()); + logger.debug("{}", anException.getMessage()); } Map result = invocation.getOutputMap(); diff --git a/bundles/model/org.eclipse.smarthome.model.core.test/build.properties b/bundles/model/org.eclipse.smarthome.model.core.test/build.properties index f3dab213a24..5f16605d7eb 100644 --- a/bundles/model/org.eclipse.smarthome.model.core.test/build.properties +++ b/bundles/model/org.eclipse.smarthome.model.core.test/build.properties @@ -1,3 +1,4 @@ source.. = src/test/java bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/bundles/model/org.eclipse.smarthome.model.core/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.core/META-INF/MANIFEST.MF index 1869b6aebcd..2718adedcdc 100644 --- a/bundles/model/org.eclipse.smarthome.model.core/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.core/META-INF/MANIFEST.MF @@ -20,12 +20,13 @@ Import-Package: com.google.common.base, org.eclipse.emf.ecore.util, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.service, + org.eclipse.smarthome.model.core, org.eclipse.xtext.common.types.impl, org.eclipse.xtext.resource, org.eclipse.xtext.resource.impl, org.osgi.framework, org.osgi.service.component, org.slf4j -Service-Component: OSGI-INF/folderobserver.xml, OSGI-INF/modelrepository.xml +Service-Component: OSGI-INF/*.xml Export-Package: org.eclipse.smarthome.model.core Bundle-ActivationPolicy: lazy diff --git a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/EventType.java b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/EventType.java index 1ec99313023..d5d698144dc 100644 --- a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/EventType.java +++ b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/EventType.java @@ -7,7 +7,11 @@ */ package org.eclipse.smarthome.model.core; -/** These are the event types that can occur as model repository changes */ +/** + * These are the event types that can occur as model repository changes + * + * @author Kai Kreuzer - Initial contribution + */ public enum EventType { ADDED, REMOVED, MODIFIED } diff --git a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/ModelRepositoryChangeListener.java b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/ModelRepositoryChangeListener.java index 53e78d873c6..6b5ec27b31a 100644 --- a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/ModelRepositoryChangeListener.java +++ b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/ModelRepositoryChangeListener.java @@ -7,10 +7,14 @@ */ package org.eclipse.smarthome.model.core; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public interface ModelRepositoryChangeListener { /** - * Performs dispatch of all binding configs and + * Performs dispatch of all binding configs and * fires all {@link ItemsChangeListener}s if {@code modelName} ends with "items". */ public void modelChanged(String modelName, EventType type); diff --git a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/ModelCoreActivator.java b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/ModelCoreActivator.java index 43791d3b63d..19674336831 100644 --- a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/ModelCoreActivator.java +++ b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/ModelCoreActivator.java @@ -10,6 +10,10 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class ModelCoreActivator implements BundleActivator { private static BundleContext context; diff --git a/bundles/model/org.eclipse.smarthome.model.item.runtime/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.item.runtime/META-INF/MANIFEST.MF index 752abc5619d..e4649b30991 100644 --- a/bundles/model/org.eclipse.smarthome.model.item.runtime/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.item.runtime/META-INF/MANIFEST.MF @@ -9,4 +9,4 @@ Import-Package: org.eclipse.smarthome.model.core, org.osgi.framework, org.slf4j Require-Bundle: org.eclipse.smarthome.model.item -Service-Component: OSGI-INF/runtime.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/internal/GenericItemProvider.java b/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/internal/GenericItemProvider.java index d2f1a7cc3ee..529961c883a 100644 --- a/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/internal/GenericItemProvider.java +++ b/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/internal/GenericItemProvider.java @@ -324,12 +324,12 @@ private void internalDispatchBindings(BindingConfigReader reader, String modelNa localReader.validateItemType(item.getType(), config); localReader.processBindingConfiguration(modelName, item.getType(), item.getName(), config); } catch (BindingConfigParseException e) { - logger.error("Binding configuration of type '" + bindingType + "' of item '" + item.getName() - + "' could not be parsed correctly.", e); + logger.error("Binding configuration of type '{}' of item '{}' could not be parsed correctly.", + bindingType, item.getName(), e); } catch (Exception e) { // Catch badly behaving binding exceptions and continue processing - logger.error("Binding configuration of type '" + bindingType + "' of item '" + item.getName() - + "' could not be parsed correctly.", e); + logger.error("Binding configuration of type '{}' of item '{}' could not be parsed correctly.", + bindingType, item.getName(), e); } } else { logger.trace("Couldn't find config reader for binding type '{}' > " diff --git a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyGenerator.java b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyGenerator.java index cb8b6fee356..8bd90c689c6 100644 --- a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyGenerator.java +++ b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyGenerator.java @@ -18,23 +18,23 @@ * */ public class LazyGenerator extends Generator { - - LazyLanguageConfig langConfig = null; - public void addLazyLanguage(LazyLanguageConfig langConfig) { - this.langConfig = langConfig; - super.addLanguage(langConfig); - } + LazyLanguageConfig langConfig = null; - @Override - protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor, Issues issues) { - super.checkConfigurationInternal(issues); - super.invokeInternal(ctx, monitor, issues); - } + public void addLazyLanguage(LazyLanguageConfig langConfig) { + this.langConfig = langConfig; + super.addLanguage(langConfig); + } - @Override - protected void checkConfigurationInternal(Issues issues) { + @Override + protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor, Issues issues) { + super.checkConfigurationInternal(issues); + super.invokeInternal(ctx, monitor, issues); + } - } + @Override + protected void checkConfigurationInternal(Issues issues) { + + } } diff --git a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyLanguageConfig.java b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyLanguageConfig.java index fbdb999503f..e3cfd2c8026 100644 --- a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyLanguageConfig.java +++ b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyLanguageConfig.java @@ -28,91 +28,92 @@ * */ public class LazyLanguageConfig extends LanguageConfig { - - private final static Logger LOG = Logger.getLogger(LazyLanguageConfig.class); - String uri = null; - boolean isUI = false; - @Override - public void setUri(String uri) { - this.uri = uri; - } + private final static Logger LOG = Logger.getLogger(LazyLanguageConfig.class); + String uri = null; + boolean isUI = false; - private Grammar grammar; + @Override + public void setUri(String uri) { + this.uri = uri; + } - @Override - public Grammar getGrammar() { - setUriReally(uri); - return grammar; - } + private Grammar grammar; - @Override - public void initialize(boolean isUi) { - this.isUI = isUi; - } + @Override + public Grammar getGrammar() { + setUriReally(uri); + return grammar; + } - public void initializeReally() { - super.initialize(isUI); - } + @Override + public void initialize(boolean isUi) { + this.isUI = isUi; + } - @Override - protected void validateGrammar(Grammar grammar) { - } + public void initializeReally() { + super.initialize(isUI); + } - @Override - public void generate(Grammar grammar, XpandExecutionContext ctx) { - initializeReally(); - super.generate(grammar, ctx); - } + @Override + protected void validateGrammar(Grammar grammar) { + } - @Override - public void generate(LanguageConfig config, XpandExecutionContext ctx) throws CompositeGeneratorException { - initializeReally(); - super.generate(config, ctx); - } + @Override + public void generate(Grammar grammar, XpandExecutionContext ctx) { + initializeReally(); + super.generate(grammar, ctx); + } - public void setUriReally(String uri) { - ResourceSet rs = GlobalResourceSet.getINSTANCE(); - for (String loadedResource : getLoadedResources()) { - URI loadedResourceUri = URI.createURI(loadedResource); - if (equal(loadedResourceUri.fileExtension(), "ecore")) { - IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE - .getResourceServiceProvider(loadedResourceUri); - if (resourceServiceProvider == null) { - EcoreSupportStandaloneSetup.setup(); - } - } else if (equal(loadedResourceUri.fileExtension(), "xcore")) { - IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE - .getResourceServiceProvider(loadedResourceUri); - if (resourceServiceProvider == null) { - try { - Class xcore = Class.forName("org.eclipse.emf.ecore.xcore.XcoreStandaloneSetup"); - xcore.getDeclaredMethod("doSetup", new Class[0]).invoke(null); - } catch (ClassNotFoundException e) { - LOG.error("Couldn't initialize Xcore support. Is it on the classpath?"); - } catch (Exception e) { - LOG.error("Couldn't initialize Xcore support.", e); - } - } - } - Resource res = rs.getResource(loadedResourceUri, true); - if (res == null || res.getContents().isEmpty()) - LOG.error("Error loading '" + loadedResource + "'"); - else if (!res.getErrors().isEmpty()) - LOG.error("Error loading '" + loadedResource + "': " + res.getErrors().toString()); - } - EcoreUtil.resolveAll(rs); - XtextResource resource = (XtextResource) rs.getResource(URI.createURI(uri), true); - if (resource.getContents().isEmpty()) { - throw new IllegalArgumentException("Couldn't load grammar for '" + uri + "'."); - } - if (!resource.getErrors().isEmpty()) { - LOG.error(resource.getErrors()); - throw new IllegalStateException("Problem parsing '" + uri + "':" + resource.getErrors().toString()); - } + @Override + public void generate(LanguageConfig config, XpandExecutionContext ctx) throws CompositeGeneratorException { + initializeReally(); + super.generate(config, ctx); + } - final Grammar grammar = (Grammar) resource.getContents().get(0); - validateGrammar(grammar); - this.grammar = grammar; - } + public void setUriReally(String uri) { + ResourceSet rs = GlobalResourceSet.getINSTANCE(); + for (String loadedResource : getLoadedResources()) { + URI loadedResourceUri = URI.createURI(loadedResource); + if (equal(loadedResourceUri.fileExtension(), "ecore")) { + IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE + .getResourceServiceProvider(loadedResourceUri); + if (resourceServiceProvider == null) { + EcoreSupportStandaloneSetup.setup(); + } + } else if (equal(loadedResourceUri.fileExtension(), "xcore")) { + IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE + .getResourceServiceProvider(loadedResourceUri); + if (resourceServiceProvider == null) { + try { + Class xcore = Class.forName("org.eclipse.emf.ecore.xcore.XcoreStandaloneSetup"); + xcore.getDeclaredMethod("doSetup", new Class[0]).invoke(null); + } catch (ClassNotFoundException e) { + LOG.error("Couldn't initialize Xcore support. Is it on the classpath?"); + } catch (Exception e) { + LOG.error("Couldn't initialize Xcore support.", e); + } + } + } + Resource res = rs.getResource(loadedResourceUri, true); + if (res == null || res.getContents().isEmpty()) { + LOG.error("Error loading '" + loadedResource + "'"); + } else if (!res.getErrors().isEmpty()) { + LOG.error("Error loading '" + loadedResource + "': " + res.getErrors().toString()); + } + } + EcoreUtil.resolveAll(rs); + XtextResource resource = (XtextResource) rs.getResource(URI.createURI(uri), true); + if (resource.getContents().isEmpty()) { + throw new IllegalArgumentException("Couldn't load grammar for '" + uri + "'."); + } + if (!resource.getErrors().isEmpty()) { + LOG.error(resource.getErrors()); + throw new IllegalStateException("Problem parsing '" + uri + "':" + resource.getErrors().toString()); + } + + final Grammar grammar = (Grammar) resource.getContents().get(0); + validateGrammar(grammar); + this.grammar = grammar; + } } diff --git a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyStandaloneSetup.java b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyStandaloneSetup.java index c0f55b0493f..ed2db0b4425 100644 --- a/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyStandaloneSetup.java +++ b/bundles/model/org.eclipse.smarthome.model.lazygen/src/main/java/org/eclipse/smarthome/model/lazygen/LazyStandaloneSetup.java @@ -37,111 +37,114 @@ */ public class LazyStandaloneSetup extends AbstractWorkflowComponent2 { - private static ResourceSet resourceSet = GlobalResourceSet.getINSTANCE(); - private Registry registry = EPackage.Registry.INSTANCE; - - Set allgeneratedEPackages = Sets.newHashSet(); - Set allGenModelFiles = Sets.newHashSet(); - Set allEcoreFiles = Sets.newHashSet(); - - public void addGeneratedPackage(String packageName) { - allgeneratedEPackages.add(packageName); - } - - public void addGenModelFile(String modelFile) { - allGenModelFiles.add(modelFile); - } - - public void addEcoreModelFile(String modelFile) { - allEcoreFiles.add(modelFile); - } - - @Override - protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor, Issues issues) { - for (String generatedEPackage : allgeneratedEPackages) { - addRegisterGeneratedEPackage(generatedEPackage); - } - for (String genModelFile : allGenModelFiles) { - addRegisterGenModelFile(genModelFile); - } - for (String ecoreFile : allEcoreFiles) { - addRegisterEcoreFile(ecoreFile); - } - - } - - private org.apache.commons.logging.Log log = LogFactory.getLog(getClass()); - - private void addRegisterGeneratedEPackage(String interfacename) { - Class clazz = ResourceLoaderFactory.createResourceLoader().loadClass(interfacename); - if (clazz == null) - throw new ConfigurationException("Couldn't find an interface " + interfacename); - try { - EPackage pack = (EPackage) clazz.getDeclaredField("eINSTANCE").get(null); - if (registry.get(pack.getNsURI()) == null) { - registry.put(pack.getNsURI(), pack); - log.info("Adding generated EPackage '" + interfacename + "'"); - } - - } catch (Exception e) { - throw new ConfigurationException("Couldn't register " + interfacename - + ". Is it the generated EPackage interface? : " + e.getMessage()); - } - } - - private void addRegisterEcoreFile(String fileName) throws IllegalArgumentException, SecurityException { - Resource res = resourceSet.getResource(createURI(fileName), true); - if (res == null) - throw new ConfigurationException("Couldn't find resource under " + fileName); - if (!res.isLoaded()) { - try { - res.load(null); - } catch (IOException e) { - throw new ConfigurationException("Couldn't load resource under " + fileName + " : " + e.getMessage()); - } - } - List result = res.getContents(); - for (EObject object : result) { - if (object instanceof EPackage) { - registerPackage(fileName, object); - } - for (final TreeIterator it = object.eAllContents(); it.hasNext();) { - EObject child = it.next(); - if (child instanceof EPackage) { - registerPackage(fileName, child); - } - } - } - } - - private GenModelHelper createGenModelHelper() { - return new GenModelHelper(); - } - - private void addRegisterGenModelFile(String fileName) { - createGenModelHelper().registerGenModel(resourceSet, createURI(fileName)); - } - - private void registerPackage(String fileName, EObject object) { - String nsUri = ((EPackage) object).getNsURI(); - if (registry.get(nsUri) == null) { - registry.put(nsUri, object); - log.info("Adding dynamic EPackage '" + nsUri + "' from '" + fileName + "'"); - } else if (log.isDebugEnabled()) { - log.debug("Dynamic EPackage '" + nsUri + "' from '" + fileName + "' already in the registry!"); - } - } - - private URI createURI(String path) { - if (path == null) - throw new IllegalArgumentException(); - - URI uri = URI.createURI(path); - if (uri.isRelative()) { - URI resolvedURI = URI.createFileURI(new File(path).getAbsolutePath()); - return resolvedURI; - } - return uri; - } + private static ResourceSet resourceSet = GlobalResourceSet.getINSTANCE(); + private Registry registry = EPackage.Registry.INSTANCE; + + Set allgeneratedEPackages = Sets.newHashSet(); + Set allGenModelFiles = Sets.newHashSet(); + Set allEcoreFiles = Sets.newHashSet(); + + public void addGeneratedPackage(String packageName) { + allgeneratedEPackages.add(packageName); + } + + public void addGenModelFile(String modelFile) { + allGenModelFiles.add(modelFile); + } + + public void addEcoreModelFile(String modelFile) { + allEcoreFiles.add(modelFile); + } + + @Override + protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor, Issues issues) { + for (String generatedEPackage : allgeneratedEPackages) { + addRegisterGeneratedEPackage(generatedEPackage); + } + for (String genModelFile : allGenModelFiles) { + addRegisterGenModelFile(genModelFile); + } + for (String ecoreFile : allEcoreFiles) { + addRegisterEcoreFile(ecoreFile); + } + + } + + private org.apache.commons.logging.Log log = LogFactory.getLog(getClass()); + + private void addRegisterGeneratedEPackage(String interfacename) { + Class clazz = ResourceLoaderFactory.createResourceLoader().loadClass(interfacename); + if (clazz == null) { + throw new ConfigurationException("Couldn't find an interface " + interfacename); + } + try { + EPackage pack = (EPackage) clazz.getDeclaredField("eINSTANCE").get(null); + if (registry.get(pack.getNsURI()) == null) { + registry.put(pack.getNsURI(), pack); + log.info("Adding generated EPackage '" + interfacename + "'"); + } + + } catch (Exception e) { + throw new ConfigurationException("Couldn't register " + interfacename + + ". Is it the generated EPackage interface? : " + e.getMessage()); + } + } + + private void addRegisterEcoreFile(String fileName) throws IllegalArgumentException, SecurityException { + Resource res = resourceSet.getResource(createURI(fileName), true); + if (res == null) { + throw new ConfigurationException("Couldn't find resource under " + fileName); + } + if (!res.isLoaded()) { + try { + res.load(null); + } catch (IOException e) { + throw new ConfigurationException("Couldn't load resource under " + fileName + " : " + e.getMessage()); + } + } + List result = res.getContents(); + for (EObject object : result) { + if (object instanceof EPackage) { + registerPackage(fileName, object); + } + for (final TreeIterator it = object.eAllContents(); it.hasNext();) { + EObject child = it.next(); + if (child instanceof EPackage) { + registerPackage(fileName, child); + } + } + } + } + + private GenModelHelper createGenModelHelper() { + return new GenModelHelper(); + } + + private void addRegisterGenModelFile(String fileName) { + createGenModelHelper().registerGenModel(resourceSet, createURI(fileName)); + } + + private void registerPackage(String fileName, EObject object) { + String nsUri = ((EPackage) object).getNsURI(); + if (registry.get(nsUri) == null) { + registry.put(nsUri, object); + log.info("Adding dynamic EPackage '" + nsUri + "' from '" + fileName + "'"); + } else if (log.isDebugEnabled()) { + log.debug("Dynamic EPackage '" + nsUri + "' from '" + fileName + "' already in the registry!"); + } + } + + private URI createURI(String path) { + if (path == null) { + throw new IllegalArgumentException(); + } + + URI uri = URI.createURI(path); + if (uri.isRelative()) { + URI resolvedURI = URI.createFileURI(new File(path).getAbsolutePath()); + return resolvedURI; + } + return uri; + } } diff --git a/bundles/model/org.eclipse.smarthome.model.persistence.runtime/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.persistence.runtime/META-INF/MANIFEST.MF index 22eaf1aff89..0236e1322df 100644 --- a/bundles/model/org.eclipse.smarthome.model.persistence.runtime/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.persistence.runtime/META-INF/MANIFEST.MF @@ -9,4 +9,4 @@ Import-Package: org.eclipse.smarthome.model.core, org.osgi.framework, org.slf4j Require-Bundle: org.eclipse.smarthome.model.persistence -Service-Component: OSGI-INF/runtime.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/model/org.eclipse.smarthome.model.persistence.tests/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.persistence.tests/META-INF/MANIFEST.MF index 80cc76bcaad..7a37b002871 100644 --- a/bundles/model/org.eclipse.smarthome.model.persistence.tests/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.persistence.tests/META-INF/MANIFEST.MF @@ -5,7 +5,8 @@ Bundle-SymbolicName: org.eclipse.smarthome.model.persistence.tests Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.smarthome.core.items, +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.core.items, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.persistence, org.eclipse.smarthome.core.types, diff --git a/bundles/model/org.eclipse.smarthome.model.persistence/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.persistence/META-INF/MANIFEST.MF index 23d6d386008..faaf81c8d7b 100644 --- a/bundles/model/org.eclipse.smarthome.model.persistence/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.persistence/META-INF/MANIFEST.MF @@ -58,5 +58,5 @@ Export-Package: org.eclipse.smarthome.model.persistence, org.joda.time.field, org.joda.time.format, org.joda.time.tz -Service-Component: OSGI-INF/persistencemanager.xml,OSGI-INF/persistenceextension.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: lib/joda-time-2.9.2.jar,. diff --git a/bundles/model/org.eclipse.smarthome.model.script/src/org/eclipse/smarthome/model/script/actions/BusEvent.java b/bundles/model/org.eclipse.smarthome.model.script/src/org/eclipse/smarthome/model/script/actions/BusEvent.java index 3490a7ebd77..344afcd597c 100644 --- a/bundles/model/org.eclipse.smarthome.model.script/src/org/eclipse/smarthome/model/script/actions/BusEvent.java +++ b/bundles/model/org.eclipse.smarthome.model.script/src/org/eclipse/smarthome/model/script/actions/BusEvent.java @@ -79,7 +79,7 @@ static public Object sendCommand(String itemName, String commandString) { Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandString); publisher.post(ItemEventFactory.createCommandEvent(itemName, command)); } catch (ItemNotFoundException e) { - LoggerFactory.getLogger(BusEvent.class).warn("Item '" + itemName + "' does not exist."); + LoggerFactory.getLogger(BusEvent.class).warn("Item '{}' does not exist.", itemName); } } return null; @@ -142,7 +142,7 @@ static public Object postUpdate(String itemName, String stateString) { State state = TypeParser.parseState(item.getAcceptedDataTypes(), stateString); publisher.post(ItemEventFactory.createStateEvent(itemName, state)); } catch (ItemNotFoundException e) { - LoggerFactory.getLogger(BusEvent.class).warn("Item '" + itemName + "' does not exist."); + LoggerFactory.getLogger(BusEvent.class).warn("Item '{}' does not exist.", itemName); } } return null; diff --git a/bundles/model/org.eclipse.smarthome.model.sitemap.runtime/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.sitemap.runtime/META-INF/MANIFEST.MF index bcab8715f71..e4335735d9e 100644 --- a/bundles/model/org.eclipse.smarthome.model.sitemap.runtime/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.sitemap.runtime/META-INF/MANIFEST.MF @@ -9,4 +9,4 @@ Import-Package: org.eclipse.smarthome.model.core, org.osgi.framework, org.slf4j Require-Bundle: org.eclipse.smarthome.model.sitemap -Service-Component: OSGI-INF/runtime.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/model/org.eclipse.smarthome.model.sitemap/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.sitemap/META-INF/MANIFEST.MF index 9534c0dabe0..bcc925fba5b 100644 --- a/bundles/model/org.eclipse.smarthome.model.sitemap/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.sitemap/META-INF/MANIFEST.MF @@ -34,5 +34,5 @@ Export-Package: org.eclipse.smarthome.model, org.eclipse.smarthome.model.sitemap.impl, org.eclipse.smarthome.model.sitemap.util, org.eclipse.smarthome.model.validation -Service-Component: OSGI-INF/sitemapprovider.xml +Service-Component: OSGI-INF/*.xml Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/bundles/model/org.eclipse.smarthome.model.thing.runtime/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.thing.runtime/META-INF/MANIFEST.MF index 22d87880f2c..316215cfba0 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.runtime/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.thing.runtime/META-INF/MANIFEST.MF @@ -9,5 +9,5 @@ Import-Package: org.eclipse.smarthome.model.core, org.osgi.framework, org.slf4j Require-Bundle: org.eclipse.smarthome.model.thing -Service-Component: OSGI-INF/runtime.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.thing.tests/META-INF/MANIFEST.MF index 859ecf6789f..a52149d0bbe 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/META-INF/MANIFEST.MF @@ -32,7 +32,5 @@ Import-Package: groovy.lang, Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: org.eclipse.smarthome.model.thing Export-Package: org.eclipse.smarthome.model.thing -Service-Component: OSGI-INF/HueThingHandlerFactory.xml, - OSGI-INF/HueThingTypeProvider.xml, - OSGI-INF/DumbThingTypeProvider.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/DumbThingTypeProvider.java b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/DumbThingTypeProvider.java index 4a15ec43882..3ccda48405b 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/DumbThingTypeProvider.java +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/DumbThingTypeProvider.java @@ -42,7 +42,7 @@ public DumbThingTypeProvider() { new ThingType(DumbThingHandlerFactory.THING_TYPE_TEST, null, "DUMB", "Funky Thing", false, channelDefinitions, null, null, new URI("dumb:DUMB"))); } catch (Exception e) { - logger.error(e.getMessage()); + logger.error("{}", e.getMessage()); } } diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueChannelTypeProvider.java b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueChannelTypeProvider.java index d4f6f5a86f8..a1d62a064b2 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueChannelTypeProvider.java +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueChannelTypeProvider.java @@ -55,7 +55,7 @@ public TestHueChannelTypeProvider() { channelTypes = Lists.newArrayList(ctColor, ctColorTemperature, ctColorX, ctColorTemperatureX); ChannelGroupType groupX = new ChannelGroupType(GROUP_CHANNEL_GROUP_TYPE_UID, false, "Channel Group", - "Channel Group", + "Channel Group", null, Lists.newArrayList(new ChannelDefinition("foo", TestHueChannelTypeProvider.COLOR_CHANNEL_TYPE_UID), new ChannelDefinition("bar", TestHueChannelTypeProvider.COLOR_CHANNEL_TYPE_UID))); channelGroupTypes = Lists.newArrayList(groupX); diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingHandlerFactory.java b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingHandlerFactory.java index 34e06d14014..598f96f3127 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingHandlerFactory.java +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingHandlerFactory.java @@ -13,6 +13,7 @@ import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; @@ -101,12 +102,22 @@ protected ThingHandler createHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { } + + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE); + } }; } else { return new BaseThingHandler(thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { } + + @Override + public void initialize() { + updateStatus(ThingStatus.ONLINE); + } }; } diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingTypeProvider.java b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingTypeProvider.java index cfb18271234..f9f570c66ce 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingTypeProvider.java +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/test/hue/TestHueThingTypeProvider.java @@ -79,7 +79,7 @@ public TestHueThingTypeProvider() { new URI("hue", "grouped", null))); } catch (Exception e) { - logger.error(e.getMessage()); + logger.error("{}", e.getMessage()); } } diff --git a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/tests/GenericThingProviderTest4.groovy b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/tests/GenericThingProviderTest4.groovy index 59638b3c3e7..90e9181e4e9 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/tests/GenericThingProviderTest4.groovy +++ b/bundles/model/org.eclipse.smarthome.model.thing.tests/src/org/eclipse/smarthome/model/thing/tests/GenericThingProviderTest4.groovy @@ -118,6 +118,10 @@ class GenericThingProviderTest4 extends OSGiTest{ } else { return new BaseThingHandler(thing) { void handleCommand(ChannelUID arg0, Command arg1) {}; + @Override + void initialize() { + updateState(ThingStatus.ONLINE); + } } } } diff --git a/bundles/storage/org.eclipse.smarthome.storage.json.test/src/main/java/org/eclipse/smarthome/storage/json/JsonStorageServiceOSGiTest.java b/bundles/storage/org.eclipse.smarthome.storage.json.test/src/main/java/org/eclipse/smarthome/storage/json/JsonStorageServiceOSGiTest.java index 29ca8d0918b..becfd7c3461 100644 --- a/bundles/storage/org.eclipse.smarthome.storage.json.test/src/main/java/org/eclipse/smarthome/storage/json/JsonStorageServiceOSGiTest.java +++ b/bundles/storage/org.eclipse.smarthome.storage.json.test/src/main/java/org/eclipse/smarthome/storage/json/JsonStorageServiceOSGiTest.java @@ -1,3 +1,10 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.storage.json; import static org.hamcrest.CoreMatchers.*; @@ -18,6 +25,10 @@ import org.junit.Before; import org.junit.Test; +/** + * + * @author Simon Kaufmann - Initial implementation + */ public class JsonStorageServiceOSGiTest extends JavaOSGiTest { private StorageService storageService; diff --git a/bundles/storage/org.eclipse.smarthome.storage.json/META-INF/MANIFEST.MF b/bundles/storage/org.eclipse.smarthome.storage.json/META-INF/MANIFEST.MF index a3b4c34e817..a7b2fb1012d 100644 --- a/bundles/storage/org.eclipse.smarthome.storage.json/META-INF/MANIFEST.MF +++ b/bundles/storage/org.eclipse.smarthome.storage.json/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Import-Package: com.google.gson, com.google.gson.annotations, com.google.gson.reflect, com.google.gson.stream, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.storage, org.osgi.framework, diff --git a/bundles/storage/org.eclipse.smarthome.storage.mapdb/META-INF/MANIFEST.MF b/bundles/storage/org.eclipse.smarthome.storage.mapdb/META-INF/MANIFEST.MF index cc379f2bb27..61e0b50a55d 100644 --- a/bundles/storage/org.eclipse.smarthome.storage.mapdb/META-INF/MANIFEST.MF +++ b/bundles/storage/org.eclipse.smarthome.storage.mapdb/META-INF/MANIFEST.MF @@ -10,6 +10,7 @@ Import-Package: com.google.gson, com.google.gson.internal.bind, com.google.gson.reflect, com.google.gson.stream, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.storage, org.mapdb, diff --git a/bundles/test/org.eclipse.smarthome.magic.test/about.html b/bundles/test/org.eclipse.smarthome.magic.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/bundles/test/org.eclipse.smarthome.magic.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

July 24, 2017

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/bundles/test/org.eclipse.smarthome.magic.test/build.properties b/bundles/test/org.eclipse.smarthome.magic.test/build.properties index 08b498dab4a..1343ecc1dfe 100644 --- a/bundles/test/org.eclipse.smarthome.magic.test/build.properties +++ b/bundles/test/org.eclipse.smarthome.magic.test/build.properties @@ -1,4 +1,6 @@ source.. = src/test/java/ output.. = target/classes bin.includes = META-INF/,\ - ., \ No newline at end of file + .,\ + about.html + diff --git a/bundles/test/org.eclipse.smarthome.magic/ESH-INF/i18n/magic_de.properties b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/i18n/magic_de.properties new file mode 100644 index 00000000000..4b7bfc446d5 --- /dev/null +++ b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/i18n/magic_de.properties @@ -0,0 +1,5 @@ +channel-type.magic.alert.label = Alarm +channel-type.magic.alert.description = Ermöglicht ein temporäres Blinken. +channel-type.magic.alert.state.option.NONE = Kein Blinken +channel-type.magic.alert.state.option.SELECT = Einmaliges Blinken +channel-type.magic.alert.state.option.LSELECT = Mehrfaches Blinken diff --git a/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/channel-types.xml b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/channel-types.xml index 59342f3f786..459b98c3f46 100644 --- a/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/channel-types.xml +++ b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/channel-types.xml @@ -20,17 +20,30 @@ The color channel allows to control the color of a light. It is also possible to dim values and switch the light on and off. - + ColorLight Lighting + + String + + The alert channel allows a temporary change to the bulb’s state. + + + + + + + + + Contact - \ No newline at end of file + diff --git a/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/thing-types.xml b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/thing-types.xml index 3d92582f0db..73a2d937834 100644 --- a/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/thing-types.xml +++ b/bundles/test/org.eclipse.smarthome.magic/ESH-INF/thing/thing-types.xml @@ -33,6 +33,7 @@ + diff --git a/bundles/test/org.eclipse.smarthome.magic/META-INF/MANIFEST.MF b/bundles/test/org.eclipse.smarthome.magic/META-INF/MANIFEST.MF index fc4c5b599a4..3010a4938c2 100644 --- a/bundles/test/org.eclipse.smarthome.magic/META-INF/MANIFEST.MF +++ b/bundles/test/org.eclipse.smarthome.magic/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: . Import-Package: com.google.common.collect, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.thing, diff --git a/bundles/test/org.eclipse.smarthome.magic/README.md b/bundles/test/org.eclipse.smarthome.magic/README.md index 15fb9f99900..7fb22a16f9e 100644 --- a/bundles/test/org.eclipse.smarthome.magic/README.md +++ b/bundles/test/org.eclipse.smarthome.magic/README.md @@ -3,6 +3,7 @@ The Magic Bundle is a virtual device bundle which provides different Things, Channels and supporting functionality for easy UI testing. Future plans: + * Firmware update support * Simulate communication errors * Provide REST API to update thing status from _outside_ @@ -29,6 +30,7 @@ The provided Things need no parameters right now. ## Channels Available channels: + * switch - the on/off toggle maps to a Switch item. * brightness - the brightness value maps to a Dimmer item. * color - the color maps to a Color item. diff --git a/bundles/test/org.eclipse.smarthome.magic/src/main/java/org/eclipse/smarthome/magic/binding/internal/MagicServiceImpl.java b/bundles/test/org.eclipse.smarthome.magic/src/main/java/org/eclipse/smarthome/magic/binding/internal/MagicServiceImpl.java index 514b2300e6e..ead4502e2a7 100644 --- a/bundles/test/org.eclipse.smarthome.magic/src/main/java/org/eclipse/smarthome/magic/binding/internal/MagicServiceImpl.java +++ b/bundles/test/org.eclipse.smarthome.magic/src/main/java/org/eclipse/smarthome/magic/binding/internal/MagicServiceImpl.java @@ -17,6 +17,10 @@ import com.google.common.collect.Lists; +/** + * + * @author Henning Treu - Initial contribution + */ public class MagicServiceImpl implements MagicService { static final String PARAMETER_BACKEND_DECIMAL = "select_decimal_limit"; diff --git a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF index eab37b11c05..9e8637676e4 100644 --- a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF +++ b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF @@ -16,11 +16,14 @@ Import-Package: com.google.common.collect, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.xml.osgi, org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.i18n;resolution:=optional, org.eclipse.smarthome.core.service, org.eclipse.smarthome.core.storage, + org.eclipse.smarthome.test, + org.eclipse.smarthome.test.java, org.eclipse.smarthome.test.storage, org.hamcrest;core=split, org.junit;version="4.0.0", diff --git a/bundles/ui/org.eclipse.smarthome.ui.icon.test/about.html b/bundles/ui/org.eclipse.smarthome.ui.icon.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/bundles/ui/org.eclipse.smarthome.ui.icon.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

July 24, 2017

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/bundles/ui/org.eclipse.smarthome.ui.icon.test/build.properties b/bundles/ui/org.eclipse.smarthome.ui.icon.test/build.properties index 6f5943edff6..626d0527cbf 100644 --- a/bundles/ui/org.eclipse.smarthome.ui.icon.test/build.properties +++ b/bundles/ui/org.eclipse.smarthome.ui.icon.test/build.properties @@ -1,4 +1,5 @@ source.. = src/test/groovy/ output.. = target/test-classes/ bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/bundles/ui/org.eclipse.smarthome.ui.test/META-INF/MANIFEST.MF b/bundles/ui/org.eclipse.smarthome.ui.test/META-INF/MANIFEST.MF index 60d8ecaa8eb..80e48d0a145 100644 --- a/bundles/ui/org.eclipse.smarthome.ui.test/META-INF/MANIFEST.MF +++ b/bundles/ui/org.eclipse.smarthome.ui.test/META-INF/MANIFEST.MF @@ -8,7 +8,9 @@ Bundle-License: http://www.eclipse.org/legal/epl-v10.html Bundle-SymbolicName: org.eclipse.smarthome.ui.test Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: . -Import-Package: org.hamcrest;core=split, - org.mockito +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.hamcrest;core=split, + org.mockito, + org.objenesis Require-Bundle: org.junit, org.mockito,org.hamcrest diff --git a/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImplTest.java b/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImplTest.java index 23cdcd60c06..56d909a1fb9 100644 --- a/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImplTest.java +++ b/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImplTest.java @@ -12,6 +12,8 @@ import static org.mockito.Mockito.*; import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.List; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.smarthome.core.items.Item; @@ -26,6 +28,7 @@ import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.model.sitemap.Mapping; import org.eclipse.smarthome.model.sitemap.Sitemap; @@ -444,4 +447,44 @@ public void getLabel_labelWithEmptyPattern() throws ItemNotFoundException { assertEquals("Label", label); } + @Test + public void getLabel_labelWithMappedOption() throws ItemNotFoundException { + String testLabel = "Label"; + Widget w = mock(Widget.class); + Item item = mock(Item.class); + StateDescription stateDescription = mock(StateDescription.class); + List options = new ArrayList<>(); + options.add(new StateOption("State0", "This is the state 0")); + options.add(new StateOption("State1", "This is the state 1")); + when(w.getLabel()).thenReturn(testLabel); + when(w.getItem()).thenReturn("Item"); + when(registry.getItem("Item")).thenReturn(item); + when(item.getStateDescription()).thenReturn(stateDescription); + when(stateDescription.getPattern()).thenReturn("%s"); + when(stateDescription.getOptions()).thenReturn(options); + when(item.getState()).thenReturn(new StringType("State1")); + String label = uiRegistry.getLabel(w); + assertEquals("Label [This is the state 1]", label); + } + + @Test + public void getLabel_labelWithUnmappedOption() throws ItemNotFoundException { + String testLabel = "Label"; + Widget w = mock(Widget.class); + Item item = mock(Item.class); + StateDescription stateDescription = mock(StateDescription.class); + List options = new ArrayList<>(); + options.add(new StateOption("State0", "This is the state 0")); + options.add(new StateOption("State1", "This is the state 1")); + when(w.getLabel()).thenReturn(testLabel); + when(w.getItem()).thenReturn("Item"); + when(registry.getItem("Item")).thenReturn(item); + when(item.getStateDescription()).thenReturn(stateDescription); + when(stateDescription.getPattern()).thenReturn("%s"); + when(stateDescription.getOptions()).thenReturn(options); + when(item.getState()).thenReturn(new StringType("State")); + String label = uiRegistry.getLabel(w); + assertEquals("Label [State]", label); + } + } diff --git a/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletServiceTest.java b/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletServiceTest.java new file mode 100644 index 00000000000..dc7a63f32a8 --- /dev/null +++ b/bundles/ui/org.eclipse.smarthome.ui.test/src/test/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletServiceTest.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.ui.internal.proxy; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.util.B64Code; +import org.eclipse.jetty.util.StringUtil; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for the {@link ProxyServletService} class. + * + * @author Kai Kreuzer - Initial contribution + * + */ +public class ProxyServletServiceTest { + + static private ProxyServletService service; + + @Before + public void setUp() { + service = new ProxyServletService(); + } + + @Test + public void testMaybeAppendAuthHeaderWithFullCredentials() throws URISyntaxException { + Request request = mock(Request.class); + URI uri = new URI("http://testuser:testpassword@127.0.0.1:8080/content"); + service.maybeAppendAuthHeader(uri, request); + verify(request).header(HttpHeader.AUTHORIZATION, + "Basic " + B64Code.encode("testuser:testpassword", StringUtil.__ISO_8859_1)); + } + + @Test + public void testMaybeAppendAuthHeaderWithoutPassword() throws URISyntaxException { + Request request = mock(Request.class); + URI uri = new URI("http://testuser@127.0.0.1:8080/content"); + service.maybeAppendAuthHeader(uri, request); + verify(request).header(HttpHeader.AUTHORIZATION, + "Basic " + B64Code.encode("testuser:", StringUtil.__ISO_8859_1)); + } + + @Test + public void testMaybeAppendAuthHeaderWithoutCredentials() throws URISyntaxException { + Request request = mock(Request.class); + URI uri = new URI("http://127.0.0.1:8080/content"); + service.maybeAppendAuthHeader(uri, request); + verify(request, never()).header(any(HttpHeader.class), anyString()); + } +} diff --git a/bundles/ui/org.eclipse.smarthome.ui/META-INF/MANIFEST.MF b/bundles/ui/org.eclipse.smarthome.ui/META-INF/MANIFEST.MF index 7e32a0661ec..50cd91bcff4 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/META-INF/MANIFEST.MF +++ b/bundles/ui/org.eclipse.smarthome.ui/META-INF/MANIFEST.MF @@ -15,6 +15,7 @@ Import-Package: javax.imageio, org.eclipse.emf.common.util, org.eclipse.emf.ecore, org.eclipse.emf.ecore.util, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.jetty.client, org.eclipse.jetty.client.api, org.eclipse.jetty.client.util, @@ -33,6 +34,8 @@ Import-Package: javax.imageio, org.eclipse.smarthome.model.core, org.eclipse.smarthome.model.items, org.eclipse.smarthome.model.sitemap, + org.eclipse.smarthome.ui.chart, + org.eclipse.smarthome.ui.items, org.osgi.framework, org.osgi.service.cm, org.osgi.service.http, @@ -43,5 +46,4 @@ Bundle-SymbolicName: org.eclipse.smarthome.ui Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: org.eclipse.smarthome.ui.chart, org.eclipse.smarthome.ui.items -Service-Component: OSGI-INF/genericitemuiprovider.xml, OSGI-INF/itemuiregistry.xml, - OSGI-INF/proxy.xml, OSGI-INF/chartservlet.xml, OSGI-INF/chartprovider.xml +Service-Component: OSGI-INF/*.xml diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/UIActivator.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/UIActivator.java index d380fd1bc25..0dfb80a9194 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/UIActivator.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/UIActivator.java @@ -12,6 +12,8 @@ /** * Extension of the default OSGi bundle activator + * + * @author Kai Kreuzer - Initial contribution */ public final class UIActivator implements BundleActivator { diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/ChartServlet.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/ChartServlet.java index f7007d18489..d86ee95910e 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/ChartServlet.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/chart/ChartServlet.java @@ -256,7 +256,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser req.getParameter("items"), req.getParameter("groups")); ImageIO.write(chart, provider.getChartType().toString(), res.getOutputStream()); } catch (ItemNotFoundException e) { - logger.debug(e.getMessage()); + logger.debug("{}", e.getMessage()); } catch (IllegalArgumentException e) { logger.warn("Illegal argument in chart: {}", e.getMessage()); } diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/GenericItemUIProvider.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/GenericItemUIProvider.java index 0d77b0fb818..be808d4d3cf 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/GenericItemUIProvider.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/GenericItemUIProvider.java @@ -15,6 +15,10 @@ import org.eclipse.smarthome.model.sitemap.Widget; import org.eclipse.smarthome.ui.items.ItemUIProvider; +/** + * + * @author Kai Kreuzer - Initial contribution + */ public class GenericItemUIProvider implements ItemUIProvider { private ModelRepository modelRepository = null; diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java index 79d328d5f76..dfd67d683fd 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java @@ -23,6 +23,7 @@ import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.items.GroupItem; @@ -48,11 +49,13 @@ import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.PlayPauseType; +import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.transform.TransformationException; import org.eclipse.smarthome.core.transform.TransformationHelper; import org.eclipse.smarthome.core.transform.TransformationService; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; import org.eclipse.smarthome.core.types.Type; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.model.sitemap.ColorArray; @@ -268,6 +271,7 @@ private Switch createPlayerButtons() { @Override public String getLabel(Widget w) { String label = getLabelFromWidget(w); + String labelMappedOption = null; // now insert the value, if the state is a string or decimal value and there is some formatting pattern defined // in the label @@ -276,11 +280,21 @@ public String getLabel(Widget w) { if (itemName != null) { State state = null; String formatPattern = getFormatPattern(label); + StateDescription stateDescription = null; try { final Item item = getItem(itemName); + // There is a known issue in the implementation of the method getStateDescription() of class Item + // in the following case: + // - the item provider returns as expected a state description without pattern but with for + // example a min value because a min value is set in the item definition but no label with + // pattern is set. + // - the channel state description provider returns as expected a state description with a pattern + // In this case, the result is no display of value by UIs because no pattern is set in the + // returned StateDescription. What is expected is the display of a value using the pattern + // provided by the channel state description provider. + stateDescription = item.getStateDescription(); if (formatPattern == null) { - final StateDescription stateDescription = item.getStateDescription(); if (stateDescription != null) { final String pattern = stateDescription.getPattern(); if (pattern != null) { @@ -321,6 +335,27 @@ public String getLabel(Widget w) { if (state == null || state instanceof UnDefType) { formatPattern = formatUndefined(formatPattern); } else if (state instanceof Type) { + // if the channel contains options, we build a label with the mapped option value + if (stateDescription != null && stateDescription.getOptions() != null) { + for (StateOption option : stateDescription.getOptions()) { + if (option.getValue().equals(state.toString()) && option.getLabel() != null) { + State stateOption = new StringType(option.getLabel()); + try { + String formatPatternOption = stateOption.format(formatPattern); + labelMappedOption = label.trim(); + labelMappedOption = labelMappedOption.substring(0, + labelMappedOption.indexOf("[") + 1) + formatPatternOption + "]"; + } catch (IllegalArgumentException e) { + logger.warn( + "Exception while formatting value '{}' of item {} with format '{}': {}", + stateOption, itemName, formatPattern, e); + labelMappedOption = null; + } + break; + } + } + } + // The following exception handling has been added to work around a Java bug with formatting // numbers. See http://bugs.sun.com/view_bug.do?bug_id=6476425 // Without this catch, the whole sitemap, or page can not be displayed! @@ -340,7 +375,7 @@ public String getLabel(Widget w) { } } - label = transform(label); + label = transform(label, labelMappedOption); return label; } @@ -401,8 +436,10 @@ protected String formatUndefined(String formatPattern) { * label (the right side is signified by being enclosed in square brackets []. * If so, check if the value starts with the call to a transformation service * (e.g. "[MAP(en.map):%s]") and execute the transformation in this case. + * If the value does not start with the call to a transformation service, + * we return the label with the mapped option value if provided (not null). */ - private String transform(String label) { + private String transform(String label, String labelMappedOption) { if (getFormatPattern(label) != null) { Matcher matcher = EXTRACT_TRANSFORMFUNCTION_PATTERN.matcher(label); if (matcher.find()) { @@ -416,8 +453,8 @@ private String transform(String label) { label = label.substring(0, label.indexOf("[") + 1) + transformation.transform(pattern, value) + "]"; } catch (TransformationException e) { - logger.error("transformation throws exception [transformation=" + transformation + ", value=" - + value + "]", e); + logger.error("transformation throws exception [transformation={}, value={}]", transformation, + value, e); label = label.substring(0, label.indexOf("[") + 1) + value + "]"; } } else { @@ -426,6 +463,8 @@ private String transform(String label) { type); label = label.substring(0, label.indexOf("[") + 1) + value + "]"; } + } else if (labelMappedOption != null) { + label = labelMappedOption; } } return label; @@ -574,8 +613,9 @@ private Widget resolveDefault(Widget widget) { if (!(widget instanceof Default)) { return widget; } else { - if (widget.getItem() != null) { - Item item = itemRegistry.get(widget.getItem()); + String itemName = widget.getItem(); + if (itemName != null) { + Item item = itemRegistry.get(itemName); if (item != null) { Widget defaultWidget = getDefaultWidget(item.getClass(), item.getName()); if (defaultWidget != null) { @@ -610,18 +650,22 @@ private EList getDynamicGroupChildren(Group group) { EList children = new BasicEList(); String itemName = group.getItem(); try { - Item item = getItem(itemName); - if (item instanceof GroupItem) { - GroupItem groupItem = (GroupItem) item; - for (Item member : groupItem.getMembers()) { - Widget widget = getDefaultWidget(member.getClass(), member.getName()); - if (widget != null) { - widget.setItem(member.getName()); - children.add(widget); + if (itemName != null) { + Item item = getItem(itemName); + if (item instanceof GroupItem) { + GroupItem groupItem = (GroupItem) item; + for (Item member : groupItem.getMembers()) { + Widget widget = getDefaultWidget(member.getClass(), member.getName()); + if (widget != null) { + widget.setItem(member.getName()); + children.add(widget); + } } + } else { + logger.warn("Item '{}' is not a group.", item.getName()); } } else { - logger.warn("Item '{}' is not a group.", item.getName()); + logger.warn("Group does not specify an associated item - ignoring it."); } } catch (ItemNotFoundException e) { logger.warn("Group '{}' could not be found.", group.getLabel(), e); @@ -630,7 +674,7 @@ private EList getDynamicGroupChildren(Group group) { } - private Class getItemType(String itemName) { + private Class getItemType(@NonNull String itemName) { try { Item item = itemRegistry.getItem(itemName); return item.getClass(); @@ -649,7 +693,7 @@ public State getItemState(String itemName) { } } - public String getItemCategory(String itemName) { + public String getItemCategory(@NonNull String itemName) { try { Item item = itemRegistry.getItem(itemName); return item.getCategory(); @@ -663,7 +707,7 @@ public Item getItem(String name) throws ItemNotFoundException { if (itemRegistry != null) { return itemRegistry.getItem(name); } else { - return null; + throw new ItemNotFoundException(name); } } @@ -672,10 +716,11 @@ public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotU if (itemRegistry != null) { return itemRegistry.getItemByPattern(name); } else { - return null; + throw new ItemNotFoundException(name); } } + @SuppressWarnings("null") @Override public Collection getItems() { if (itemRegistry != null) { @@ -685,6 +730,7 @@ public Collection getItems() { } } + @SuppressWarnings("null") @Override public Collection getItemsOfType(String type) { if (itemRegistry != null) { @@ -694,6 +740,7 @@ public Collection getItemsOfType(String type) { } } + @SuppressWarnings("null") @Override public Collection getItems(String pattern) { if (itemRegistry != null) { @@ -814,7 +861,7 @@ private boolean matchStateToValue(State state, String value, String matchConditi break; } } catch (NumberFormatException e) { - logger.debug("matchStateToValue: Decimal format exception: " + e); + logger.debug("matchStateToValue: Decimal format exception: ", e); } } else if (state instanceof DateTimeType) { Calendar val = ((DateTimeType) state).getCalendar(); @@ -856,7 +903,7 @@ private boolean matchStateToValue(State state, String value, String matchConditi break; } } catch (NumberFormatException e) { - logger.debug("matchStateToValue: Decimal format exception: " + e); + logger.debug("matchStateToValue: Decimal format exception: ", e); } } else { // Strings only allow = and != @@ -905,13 +952,14 @@ private String processColorDefinition(State state, List colorList) { continue; } - // If there's an item defined here, get it's state - if (color.getItem() != null) { + // If there's an item defined here, get its state + String itemName = color.getItem(); + if (itemName != null) { // Try and find the item to test. // If it's not found, return visible Item item; try { - item = itemRegistry.getItem(color.getItem()); + item = itemRegistry.getItem(itemName); // Get the item state cmpState = item.getState(); @@ -972,7 +1020,8 @@ public boolean getVisiblity(Widget w) { logger.debug("Checking visiblity for widget '{}'.", w.getLabel()); for (VisibilityRule rule : w.getVisibility()) { - if (rule.getItem() == null) { + String itemName = rule.getItem(); + if (itemName == null) { continue; } if (rule.getState() == null) { @@ -983,7 +1032,7 @@ public boolean getVisiblity(Widget w) { // If it's not found, return visible Item item; try { - item = itemRegistry.getItem(rule.getItem()); + item = itemRegistry.getItem(itemName); } catch (ItemNotFoundException e) { logger.error("Cannot retrieve visibility item {} for widget {}", rule.getItem(), w.eClass().getInstanceTypeName()); @@ -1047,6 +1096,7 @@ public String toString() { } } + @SuppressWarnings("null") @Override public Collection getItemsByTag(String... tags) { if (itemRegistry != null) { @@ -1056,6 +1106,7 @@ public Collection getItemsByTag(String... tags) { } } + @SuppressWarnings("null") @Override public Collection getItemsByTagAndType(String type, String... tags) { if (itemRegistry != null) { @@ -1065,6 +1116,7 @@ public Collection getItemsByTagAndType(String type, String... tags) { } } + @SuppressWarnings("null") @Override public Collection getItemsByTag(Class typeFilter, String... tags) { if (itemRegistry != null) { diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/AsyncProxyServlet.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/AsyncProxyServlet.java index dd3823cf88f..3907c7cd730 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/AsyncProxyServlet.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/AsyncProxyServlet.java @@ -48,13 +48,10 @@ protected HttpClient newHttpClient() { /** * Add Basic Authentication header to request if user and password are specified in URI. - * - * After Jetty is upgraded past 9.2.9, change to copyRequestHeaders to avoid deprecation warning. */ - @SuppressWarnings("deprecation") @Override - protected void copyHeaders(HttpServletRequest clientRequest, Request proxyRequest) { - super.copyHeaders(clientRequest, proxyRequest); + protected void copyRequestHeaders(HttpServletRequest clientRequest, Request proxyRequest) { + super.copyRequestHeaders(clientRequest, proxyRequest); service.maybeAppendAuthHeader(service.uriFromRequest(clientRequest), proxyRequest); } diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletService.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletService.java index 174e4989537..be8a89ae0ce 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletService.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/proxy/ProxyServletService.java @@ -278,7 +278,7 @@ URI uriFromRequest(HttpServletRequest request) { } /** - * If the URI contains user info in the form user:pass, attempt to preempt the server + * If the URI contains user info in the form user[:pass]@, attempt to preempt the server * returning a 401 by providing Basic Authentication support in the initial request to the server. * * @param uri the URI which may contain user info @@ -288,11 +288,12 @@ void maybeAppendAuthHeader(URI uri, Request request) { if (uri != null && uri.getUserInfo() != null) { String[] userInfo = uri.getUserInfo().split(":"); - if (userInfo.length >= 2) { + if (userInfo.length >= 1) { String user = userInfo[0]; - String password = userInfo[1]; + String password = userInfo.length >= 2 ? userInfo[1] : null; + String authString = password != null ? user + ":" + password : user + ":"; - String basicAuthentication = "Basic " + B64Code.encode(user + ":" + password, StringUtil.__ISO_8859_1); + String basicAuthentication = "Basic " + B64Code.encode(authString, StringUtil.__ISO_8859_1); request.header(HttpHeader.AUTHORIZATION, basicAuthentication); } } diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIProvider.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIProvider.java index 94ed237c9fc..53ec05621e0 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIProvider.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIProvider.java @@ -7,6 +7,8 @@ */ package org.eclipse.smarthome.ui.items; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.model.sitemap.Widget; @@ -25,7 +27,7 @@ public interface ItemUIProvider { * @param itemName the name of the item to return the icon for * @return the name of the category to use or null if undefined. */ - public String getCategory(String itemName); + public @Nullable String getCategory(@NonNull String itemName); /** * Returns the label text to be used for an item in the UI. @@ -33,31 +35,32 @@ public interface ItemUIProvider { * @param item the name of the item to return the label text for * @return the label text to be used in the UI or null if undefined. */ - public String getLabel(String itemName); + public @Nullable String getLabel(@NonNull String itemName); /** * Provides a default widget for a given item (class). This is used whenever * the UI needs to be created dynamically and there is no other source * of information about the widgets. * - * @param itemType the class of the item + * @param itemType the class of the item or null, if unknown * @param itemName the item name to get the default widget for * - * @return a widget implementation that can be used for the given item + * @return a widget implementation that can be used for the given item or null, if no default is available for the + * type */ - public Widget getDefaultWidget(Class itemType, String itemName); + public @Nullable Widget getDefaultWidget(@Nullable Class itemType, @NonNull String itemName); /** *

* Provides a widget for a given item. This can be used to overwrite the widget listed in the sitemap. A use case * for this is that the sitemap defines merely the parent-child-relation of widgets, but the concrete widget to be * used for rendering might be selected dynamically at runtime. - * + * *

* If the sitemap widget should not be overridden, this method must return null. * * @param itemName the item name to get the widget for * @return a widget to use for the given item or null if sitemap should not be overridden. */ - public Widget getWidget(String itemName); + public @Nullable Widget getWidget(@NonNull String itemName); } diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java index 469c8eb0f23..de13762fbbe 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java @@ -9,6 +9,7 @@ import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.items.ItemRegistry; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.model.sitemap.LinkableWidget; @@ -41,7 +42,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to retrieve the label for * @return the label to use for the widget */ - public String getLabel(Widget w); + public String getLabel(@NonNull Widget w); /** * Retrieves the category for a widget. @@ -54,7 +55,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to retrieve the category for * @return the category to use for the widget */ - public String getCategory(Widget w); + public String getCategory(@NonNull Widget w); /** * Retrieves the current state of the item of a widget or UnDefType.UNDEF. @@ -63,7 +64,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to retrieve the item state for * @return the item state of the widget */ - public State getState(Widget w); + public State getState(@NonNull Widget w); /** * Retrieves the widget for a given id on a given sitemap. @@ -74,7 +75,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the id of the widget to look for * @return the widget for the given id */ - public Widget getWidget(Sitemap sitemap, String id); + public Widget getWidget(@NonNull Sitemap sitemap, @NonNull String id); /** * Provides an id for a widget. @@ -89,7 +90,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to get the id for * @return an id for this widget */ - public String getWidgetId(Widget w); + public String getWidgetId(@NonNull Widget w); /** * this should be used instead of Sitemap.getChildren() as the default @@ -99,7 +100,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the sitemap to retrieve the children for * @return the children of the sitemap */ - public EList getChildren(Sitemap sitemap); + public EList getChildren(@NonNull Sitemap sitemap); /** * this should be used instead of LinkableWidget.getChildren() as there @@ -110,7 +111,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to retrieve the children for * @return the (dynamically or statically defined) children of the widget */ - public EList getChildren(LinkableWidget w); + public EList getChildren(@NonNull LinkableWidget w); /** * this should be used instead of Widget.eContainer() as as the concrete @@ -120,7 +121,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * the widget to retrieve the parent for * @return the parent of the widget */ - public EObject getParent(Widget w); + public EObject getParent(@NonNull Widget w); /** * Gets the label color for the widget. Checks conditional statements to @@ -130,7 +131,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * Widget * @return String with the color */ - public String getLabelColor(Widget w); + public String getLabelColor(@NonNull Widget w); /** * Gets the value color for the widget. Checks conditional statements to @@ -140,7 +141,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * Widget * @return String with the color */ - public String getValueColor(Widget w); + public String getValueColor(@NonNull Widget w); /** * Gets the widget visibility based on the item state @@ -149,7 +150,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * Widget * @return true if the item is visible */ - public boolean getVisiblity(Widget w); + public boolean getVisiblity(@NonNull Widget w); /** * Gets the item state @@ -158,5 +159,5 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * item name * @return State of the item */ - public State getItemState(String itemName); + public State getItemState(@NonNull String itemName); } diff --git a/docs/_includes/documentation-menu.html b/docs/_includes/documentation-menu.html index e0e7b8f0f7f..8b70f059dbe 100644 --- a/docs/_includes/documentation-menu.html +++ b/docs/_includes/documentation-menu.html @@ -16,6 +16,7 @@

  • REST API
  • Textual Configuration
  • Internationalization
  • +
  • Framework Utilities
  • Rules
  • Bindings
  • @@ -68,4 +70,4 @@
  • Contributing
  • Downloads
  • - \ No newline at end of file + diff --git a/docs/documentation/concepts/discovery.md b/docs/documentation/concepts/discovery.md index 537a6197658..e9a5d55e091 100644 --- a/docs/documentation/concepts/discovery.md +++ b/docs/documentation/concepts/discovery.md @@ -15,3 +15,9 @@ In Eclipse SmartHome bindings therefore implement _Discovery Services_ for thing The inbox holds a list of all discovered things (`DiscoveryResult`) from all active discovery services. A discovery result represents a discovered thing of a specific thing type, that could be instantiated as a thing. The result usually contains properties that identify the discovered things further like IP address or a serial number. Each discovery result also has a timestamp when it was added to or updated in the inbox and it may also contain a time to live, indicating the time after which it is be automatically removed from the inbox. Discovery results can either be ignored or approved, where in the latter case a thing is created for them and they become available in the application. If an entry is ignored, it will be hidden in the inbox without creating a thing for it. + +Eclipse SmartHome offers a service to automatically ignore discovery results in the inbox, whenever a thing is created manually, that represents the same thing, as the respective discovery result would create. + +This thing would either have the same thing UID or the value of its representation property is equal to the representation property's value in the discovery result. + +This service is enabled by default but could be disabled by setting `autoIgnore` to false. diff --git a/docs/documentation/concepts/items.md b/docs/documentation/concepts/items.md index 40bca450d62..e877fae773d 100644 --- a/docs/documentation/concepts/items.md +++ b/docs/documentation/concepts/items.md @@ -78,6 +78,6 @@ Here is a short table demonstrating conversions for the examples above: | Itemname | Main data type | Additional data types conversions | | --- | --- | --- | -| Color | `HSBType` |
    • `OnOffType` - `OFF` if the brightness level in the `HSBType` equals 0, `ON` otherwise
    • `PercentType` - the value for the brightness level in the `HSBType`
    | +| Color | `HSBType` | • `OnOffType` - `OFF` if the brightness level in the `HSBType` equals 0, `ON` otherwise
    • `PercentType` - the value for the brightness level in the `HSBType` | | Dimmer | `PercentType` | `OnOffType` - `OFF` if the brightness level indicated by the percent type equals 0, `ON` otherwise | | Rollershutter | `PercentType` | `UpDownType` - `UP` if the shutter level indicated by the percent type equals 0, `DOWN` if it equals 100, and `UnDefType.UNDEF` for any other value| diff --git a/docs/documentation/concepts/things.md b/docs/documentation/concepts/things.md index b9e46c9d5f3..b75ca00b2c1 100644 --- a/docs/documentation/concepts/things.md +++ b/docs/documentation/concepts/things.md @@ -4,21 +4,39 @@ layout: documentation # Things -Things are the entities that can physically be added to a system and which can potentially provide many functionalities in one. It is important to note that things do not have to be devices, but they can also represent a web service or any other manageable source of information and functionality. +Things are the entities that can physically be added to a system and which can potentially provide many functionalities in one. +It is important to note that things do not have to be devices, but they can also represent a web service or any other manageable source of information and functionality. From a user perspective, they are relevant for the setup and configuration process, but not for the operation. -Things can have configuration properties, which can be optional or mandatory. Such properties can be basic information like an IP address, an access token for a web service or a device specific configuration that alters its behavior. +Things can have configuration properties, which can be optional or mandatory. +Such properties can be basic information like an IP address, an access token for a web service or a device specific configuration that alters its behavior. -Things provide "channels", which represent the different functions they provide. Channels are linked to items, where such links are the glue between the virtual and the physical layer. Once such a link is established, a thing reacts on events sent for an item that is linked to one of its channels. Likewise, it actively sends out events for items linked to its channels. +### Channels -A special type of thing is a "bridge". Bridges are things that need to be added to the system in order to gain access to other things. A typical example of a bridge is an IP gateway for some non-IP based home automation system. +Things provide "channels", which represent the different functions the thing provides. +Where the thing is the physical entity or source of information, the channel is a concrete function from this thing. +A physical light bulb might have a dimmer channel and a color channel, both providing functionality of the one light bulb thing to the system. +For sources of information the thing might be the local weather with information from a web service with different channels like temperature, pressure and humidity. + +Channels are linked to items, where such links are the glue between the virtual and the physical layer. +Once such a link is established, a thing reacts on events sent for an item that is linked to one of its channels. +Likewise, it actively sends out events for items linked to its channels. + +### Bridges + +A special type of thing is a "bridge". +Bridges are things that need to be added to the system in order to gain access to other things. +A typical example of a bridge is an IP gateway for some non-IP based home automation system or a web service configuration with authentication information which every thing from this web service might need. + +### Discovery As many things can be automatically discovered, there are special mechanisms available that deal with the handling of [automatically discovered things](discovery.html). ## Thing Status -Each thing has a status object, which helps to identify possible problems with the device or service. The following table provides an overview of the different statuses: +Each thing has a status object, which helps to identify possible problems with the device or service. +The following table provides an overview of the different statuses: | Status | Description | |---------------|-------------| @@ -30,7 +48,7 @@ Each thing has a status object, which helps to identify possible problems with t | REMOVING | The device/service represented by a thing should be removed, but the binding did not confirm the deletion yet. Some bindings need to communicate with the device to unpair it from the system. Thing is probably not working and commands can not be processed. | | REMOVED | This status indicates that the device/service represented by a thing was removed from the external system after the REMOVING was initiated by the framework. Usually this status is an intermediate status because the thing gets removed from the database after this status was assigned. | -The statuses UNINITIALIZED, INITIALIZING and REMOVING are set by the framework, where as the statuses UNKNOWN, ONLINE and OFFLINE are assigned from a binding. +The statuses UNINITIALIZED, INITIALIZING and REMOVING are set by the framework, where as the statuses UNKNOWN, ONLINE and OFFLINE are assigned from a binding. Additionally, the REMOVED state is set by the binding to indicate that the removal process has been completed, i.e. the thing must have been in REMOVING state before. @@ -40,11 +58,17 @@ The following diagram shows the allowed status transitions: ![Status Transitions](diagrams/status_transitions.png) -The initial state of a thing is UNINITIALIZED. From UNINITIALIZED the thing goes into INITIALIZING. If the initialization fails, the thing goes back to UNINITIALIZED. If the initialization succeeds, the binding sets the status of the thing to UNKNOWN, ONLINE or OFFLINE, which all mean that the thing handler is fully initialized. From one of this states the thing can go back into UNINITIALIZED, REMOVING or REMOVED. The statuses REMOVING and REMOVED can also be reached from any of the other states. +The initial state of a thing is UNINITIALIZED. +From UNINITIALIZED the thing goes into INITIALIZING. +If the initialization fails, the thing goes back to UNINITIALIZED. +If the initialization succeeds, the binding sets the status of the thing to UNKNOWN, ONLINE or OFFLINE, which all mean that the thing handler is fully initialized. +From one of this states the thing can go back into UNINITIALIZED, REMOVING or REMOVED. +The statuses REMOVING and REMOVED can also be reached from any of the other states. ## Status Details -A status is detailed further with a status detail object. The following table lists the different status details for each status: +A status is detailed further with a status detail object. +The following table lists the different status details for each status: @@ -69,11 +93,14 @@ A status is detailed further with a status detail object. The following table li ### Status Description -To provide additional information about the current status a description is used. The status description is to be specified by the binding. This description can be used for debugging purposes and should not be presented to the user, as it might contain unreadable technical information (e.g. an HTTP status code, or any other protocol specific information, which helps to identify the current problem). +To provide additional information about the current status a description is used. +The status description is to be specified by the binding. +This description can be used for debugging purposes and should not be presented to the user, as it might contain unreadable technical information (e.g. an HTTP status code, or any other protocol specific information, which helps to identify the current problem). ### Thing Status API -The Thing interface defines a method `getStatusInfo()` to retrieve the current status of the thing. The following code shows how to print the status of each thing into the console: +The Thing interface defines a method `getStatusInfo()` to retrieve the current status of the thing. +The following code shows how to print the status of each thing into the console: ```java Collection things = thingRegistry.getAll(); diff --git a/docs/documentation/development/bindings/categories.md b/docs/documentation/development/bindings/categories.md new file mode 100644 index 00000000000..01844e07892 --- /dev/null +++ b/docs/documentation/development/bindings/categories.md @@ -0,0 +1,68 @@ +--- +layout: documentation +--- + +{% include base.html %} + +# Categories + +Categories in Eclipse SmartHome are used to provide meta information about things channels, etc. UIs can use this information to render specific icons or provide a search functionality to for example filter all things for a certain category. + +## Thing Categories + +The thing type definition allows to specify a category. User interfaces can parse this category to get an idea how to render this thing. A binding can classify each thing into one of the existing categories. The list of all predefined categories can be found in our categories overview: + +| Category | Description | +|-----------------|------------------------------------------------------| + + +## Channel Categories + +The channel type definition allows to specify a category. Together with the definition of the `readOnly` attribute in the state description, user interfaces get an idea how to render an item for this channel. A binding should classify each channel into one of the existing categories. This is a list of all predefined categories with their usual accessible mode and the according item type: + +| Category | Accessible Mode | Item Type | +|---------------|-----------------|------------------------| +| Alarm | R, RW | Switch | +| Battery | R | Switch, Number | +| Blinds | RW | Rollershutter | +| ColorLight | RW | Color | +| Contact | R | Contact | +| DimmableLight | RW | Dimmer | +| CarbonDioxide | R | Switch, Number | +| Door | R, RW | Switch | +| Energy | R | Number | +| Fan | RW | Switch, Number, String | +| Fire | R | Switch | +| Flow | R | Number | +| GarageDoor | RW | String | +| Gas | R | Switch, Number | +| Humidity | R | Number | +| Light | R, RW | Switch, Number | +| Moisture | R | Number | +| Motion | R | Switch | +| MoveControl | RW | String | +| Noise | R | Number | +| Player | RW | Player | +| PowerOutlet | RW | Switch | +| Pressure | R | Number | +| QualityOfService | R | Number | +| Rain | R | Switch, Number | +| Recorder | RW | String | +| Smoke | R | Switch | +| SoundVolume | R, RW | Number | +| Switch | RW | Switch | +| Temperature | R, RW | Number | +| Water | R | Switch, Number | +| Wind | R | Number | +| Window | R, RW | String, Switch | +| Zoom | RW | String | + +R=Read, RW=Read/Write + +The accessible mode indicates whether a category could have `read only` flag configured to true or not. For example the `Motion` category can be used for sensors only, so `read only` can not be false. Temperature can be either measured or adjusted, so the accessible mode is R and RW, which means the read only flag can be `true` or `false`. In addition categories are related to specific item types. For example the 'Energy' category can only be used for `Number` items. But `Rain` could be either expressed as Switch item, where it only indicates if it rains or not, or as `Number`, which gives information about the rain intensity. + +The list of categories may not be complete and not every device will fit into one of these categories. It is possible to define own categories. If the category is widely used, the list of predefined categories can be extended. Moreover, not all user interfaces will support all categories. It is more important to specify the `read only` information and state information, so that default controls can be rendered, even if the category is not supported. + +## Group Channel Categories + +Channel groups can be seen as a kind of `sub-device` as they combine certain (physical) abilities of a `thing` into one. For such `group channels` one can set a category from the `channel` category list. diff --git a/docs/documentation/development/bindings/thing-definition.md b/docs/documentation/development/bindings/thing-definition.md index 394e797a8fb..f70754a3c24 100644 --- a/docs/documentation/development/bindings/thing-definition.md +++ b/docs/documentation/development/bindings/thing-definition.md @@ -95,6 +95,7 @@ There exist systemwide channels that are available by default: | low-battery | system.low-battery | Switch | Battery | Represents a low battery warning with possible values on/off. | | battery-level | system.battery-level | Number | Battery | Represents the battery level as a percentage (0-100%). Bindings for things supporting battery level in a different format (eg 4 levels) should convert to a percentage to provide a consistent battery level reading. | +For further information about categories see the [categories page](categories.html). The `advanced` property indicates whether this channel is a basic or a more specific functionality of the thing. If `advanced` is set to `true` a user interface may hide this channel by default. The default value is `false` and thus will be taken if the `advanced` attribute is not specified. Especially for complex devices with a lot of channels, only a small set of channels - the most important ones - should be shown to the user to reduce complexity. Whether a channel should be declared as `advanced` depends on the device and can be decided by the binding developer. If a functionality is rarely used it should be better marked as `advanced`. @@ -137,14 +138,26 @@ In the following sections the declaration and semantics of tags, state descripti ### Default Tags -The XML definition of a ThingType allows to assign default tags to channels. All items bound to this channel will automatically be tagged with these default tags. The following snippet shows a weather tag definition: +The XML definition of a ThingType allows to assign default tags to channels. All items bound to this channel will automatically be tagged with these default tags. The following snippet shows a 'Lighting' tag definition: ```xml - weather + Lighting ``` +Please note that only tags from a pre-defined tag library should be used. +This library is still t.b.d., and only a very small set of tags are defined so far: + +| Tag | Item Types | Description | +|--------------------|-----------------------|---------------------------------------------------------------------------------------| +| Lighting | Switch, Dimmer, Color | A light source, either switchable, dimmable or color | +| Switchable | Switch, Dimmer, Color | An accessory that can be turned off and on. | +| CurrentTemperature | Number | An accessory that provides a single read-only temperature value. | +| TargetTemperature | Number | A target temperature that should engage a thermostats heating and cooling actions. | +| CurrentHumidity | Number | An accessory that provides a single read-only value indicating the relative humidity. | + + ### State Description The state description allows to specify restrictions and additional information for the state of an item, that is linked to the channel. Some configuration options are only valid for specific item types. The following XML snippet shows the definition for a temperature actuator channel: @@ -169,51 +182,6 @@ Some channels might have only a limited and countable set of states. These state The user interface can use these values to render labels for values or to provide a selection of states, when the channel is writable. The option labels can also be localized. -### Channel Categories - -The channel type definition allows to specify a category. Together with the definition of the `readOnly` attribute in the state description, user interfaces get an idea how to render an item for this channel. A binding should classify each channel into one of the existing categories. This is a list of all predefined categories with their usual accessible mode and the according item type: - -| Category | Accessible Mode | Item Type | -|---------------|-----------------|------------------------| -| Alarm | R, RW | Switch | -| Battery | R | Switch, Number | -| Blinds | RW | Rollershutter | -| ColorLight | RW | Color | -| Contact | R | Contact | -| DimmableLight | RW | Dimmer | -| CarbonDioxide | R | Switch, Number | -| Door | R, RW | Switch | -| Energy | R | Number | -| Fan | RW | Switch, Number, String | -| Fire | R | Switch | -| Flow | R | Number | -| GarageDoor | RW | String | -| Gas | R | Switch, Number | -| Humidity | R | Number | -| Light | R, RW | Switch, Number | -| Motion | R | Switch | -| MoveControl | RW | String | -| Player | RW | Player | -| PowerOutlet | RW | Switch | -| Pressure | R | Number | -| QualityOfService | R | Number | -| Rain | R | Switch, Number | -| Recorder | RW | String | -| Smoke | R | Switch | -| SoundVolume | R, RW | Number | -| Switch | RW | Switch | -| Temperature | R, RW | Number | -| Water | R | Switch, Number | -| Wind | R | Number | -| Window | R, RW | String, Switch | -| Zoom | RW | String | - -R=Read, RW=Read/Write - -The accessible mode indicates whether a category could have `read only` flag configured to true or not. For example the `Motion` category can be used for sensors only, so `read only` can not be false. Temperature can be either measured or adjusted, so the accessible mode is R and RW, which means the read only flag can be `true` or `false`. In addition categories are related to specific item types. For example the 'Energy' category can only be used for `Number` items. But `Rain` could be either expressed as Switch item, where it only indicates if it rains or not, or as `Number`, which gives information about the rain intensity. - -The list of categories may not be complete and not every device will fit into one of these categories. It is possible to define own categories. If the category is widely used, the list of predefined categories can be extended. Moreover, not all user interfaces will support all categories. It is more important to specify the `read only` information and state information, so that default controls can be rendered, even if the category is not supported. - ### Channel Groups Some devices might have a lot of channels. There are also complex devices like a multi-channel actuator, which is installed inside the switchboard, but controls switches in other rooms. Therefore channel groups can be used to group a set of channels together into one logical block. A thing can only have direct channels or channel groups, but not both. @@ -231,12 +199,13 @@ Inside the thing types XML file channel groups can be defined like this: ``` -The channel group type is defined on the same level as the thing types and channel types. The group type must have a label and an optional description. Moreover the list of contained channels must be specified: +The channel group type is defined on the same level as the thing types and channel types. The group type must have a label, an optional description, and an optional [category](categories.html). Moreover the list of contained channels must be specified: ```xml This is a single switch actor with a switch channel + Light diff --git a/docs/documentation/development/bindings/thing-handler.md b/docs/documentation/development/bindings/thing-handler.md index 8a7da55a398..495d5f16346 100644 --- a/docs/documentation/development/bindings/thing-handler.md +++ b/docs/documentation/development/bindings/thing-handler.md @@ -119,7 +119,7 @@ public void initialize() { // execute some binding specific polling code } }; - pollingJob = scheduler.scheduleAtFixedRate(runnable, 0, 30, TimeUnit.SECONDS); + pollingJob = scheduler.scheduleAtFixedDelay(runnable, 0, 30, TimeUnit.SECONDS); } ``` diff --git a/docs/documentation/development/bindings/xml-reference.md b/docs/documentation/development/bindings/xml-reference.md index 34fc1df96c7..dbf4f229f41 100644 --- a/docs/documentation/development/bindings/xml-reference.md +++ b/docs/documentation/development/bindings/xml-reference.md @@ -48,7 +48,7 @@ The following HTML tags are allowed -: ```,
    , ,

    ,

    ,

    ,

    {true|false} - + {network-address|password|password-create|color|date|datetime|email|month|week|dayOfWeek|time|tel|url|item|thing|group|tag|service|channel|rule|location} {true|false} String @@ -323,6 +323,12 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending ` ... + + propertyValue + ... + + propertyName + ... @@ -359,6 +365,12 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending ` ... + + propertyValue + ... + + propertyName + ... @@ -407,6 +419,7 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending ` String + String @@ -441,6 +454,8 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending `

    + + @@ -483,6 +498,7 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending ` + diff --git a/docs/documentation/development/guidelines.md b/docs/documentation/development/guidelines.md index edb1ae95f1f..0d88525a3f5 100644 --- a/docs/documentation/development/guidelines.md +++ b/docs/documentation/development/guidelines.md @@ -22,6 +22,7 @@ Note that this list also serves as a checklist for code reviews on pull requests 1. Generics must be used where applicable. 1. Code should not show any warnings. Warnings that cannot be circumvented should be suppressed by using the @SuppressWarnings annotation. 1. For dependency injection, OSGi Declarative Services should be used. +1. OSGi Declarative Services should be declared using annotations. The IDE will take care of the service *.xml file creation. See the official OSGi documentation for an [example here](http://enroute.osgi.org/services/org.osgi.service.component.html). 1. Packages that contain classes that are not meant to be used by other bundles should have "internal" in their package name. ## B. OSGi Bundles @@ -29,11 +30,12 @@ Note that this list also serves as a checklist for code reviews on pull requests 7. Every bundle must contain a Maven pom.xml with a version and artifact name that is in sync with the manifest entry. The pom.xml must reference the correct parent pom (which is usually in the parent folder). 1. Every bundle must contain an [about.html](https://eclipse.org/legal/epl/about.php) file, providing license information. 1. Every bundle must contain a build.properties file, which lists all resources that should end up in the binary under ```bin.includes```. -1. The manifest must not contain any "Require-Bundle" entries. Instead, "Import-Package" must be used. +1. The manifest must not contain any "Require-Bundle" entries (except for test fragment bundles, see below). Instead, "Import-Package" must be used. 1. The manifest must not export any internal package. 1. The manifest must not have any version constraint on package imports, unless this is thoughtfully added. Note that Eclipse automatically adds these constraints based on the version in the target platform, which might be too high in many cases. 1. The manifest must include all services in the Service-Component entry. A good approach is to put OSGI-INF/*.xml in there. 1. Every exported package of a bundle must be imported by the bundle itself again. +1. Test fragments may have the bundles `org.junit`, `org.hamcrest` and `org.mockito` in the "Require-Bundle" section. This is the only exception to not having "Require-Bundle" at all. ## C. Language Levels and Libraries diff --git a/docs/documentation/development/ide.md b/docs/documentation/development/ide.md index 85f613e8b0a..6c23e2e7c17 100644 --- a/docs/documentation/development/ide.md +++ b/docs/documentation/development/ide.md @@ -33,7 +33,7 @@ The Eclipse IDE is used for Eclipse SmartHome developments. The Eclipse Installe ![Step 7](images/ide7.png) 10. Your workspace should now fully compile and you can start the runtime by launching the "SmartHome Runtime" launch configuration: ![Step 8](images/ide8.png) -11. Access the PaperUI at [http://localhost:8080/paperui/index.html](http://localhost:8080/paperui/index.html). For more information about PaperUI see [PaperIU Development](notes.html#paperui-development-jshtml). +11. Access the Paper UI at [http://localhost:8080/paperui/index.html](http://localhost:8080/paperui/index.html). For more information about Paper UI see [Paper UI Development](notes.html#paperui-development-jshtml). Note that you will find the sources in a subfolder called "git" within your selected installation folder. You can use any kind of git client here, if you do not want to use the git support from within the Eclipse IDE. If you want to push changes, you need to do so to [your personal fork of the Eclipse SmartHome repository](https://github.com/eclipse/smarthome/fork) in order to create a pull request. You will find more details in the ["How to contribute"](../community/contributing.html) documentation. diff --git a/docs/documentation/development/images/ide1.png b/docs/documentation/development/images/ide1.png index fe2b2c3709a..d4b5777a78e 100644 Binary files a/docs/documentation/development/images/ide1.png and b/docs/documentation/development/images/ide1.png differ diff --git a/docs/documentation/development/notes.md b/docs/documentation/development/notes.md index 58aab4308ec..f96a5b062fa 100644 --- a/docs/documentation/development/notes.md +++ b/docs/documentation/development/notes.md @@ -22,7 +22,7 @@ Now you can have a look at the manifest that would be generated by the bndtool u cat target/classes/META-INF/MANIFEST.MF -PaperUI development (JS/HTML) +Paper UI development (JS/HTML) --- The initial setup of PaperUI can be done in two ways: @@ -30,12 +30,12 @@ The initial setup of PaperUI can be done in two ways: 1. By running the maven build. 2. Running an `npm install` followed by `npm run build`. -The JS/HTML files of PaperUI are packaged using the 'Gulp' plugin. There are two important gulp tasks needed for development. For normal development you need to run the following command on console: +The JS/HTML files of Paper UI are packaged using the 'Gulp' plugin. There are two important gulp tasks needed for development. For normal development you need to run the following command on console: npm start This will automatically inject all the needed files to index.html and will launch a browsersync instance running on http://localhost:3000/ (default). The calls to rest end-point are also proxied by this gulp task. After running this, any changes made into the files of folder 'web-src' will be available in the browser after refreshing the page. -The distribution version of PaperUI can be seen using the command: +The distribution version of Paper UI can be seen using the command: npm run build diff --git a/docs/documentation/development/testing.md b/docs/documentation/development/testing.md index f0006f4250e..d87257ecd97 100644 --- a/docs/documentation/development/testing.md +++ b/docs/documentation/development/testing.md @@ -6,12 +6,12 @@ layout: documentation Testing Eclipse SmartHome === -There are two different kinds of approaches for testing Eclipse SmartHome. One is to use plain JUnit tests for testing simple classes. The other is to execute JUnit tests within the OSGi environment to test OSGi services and dynamic behaviour. Both approaches are supported through a simple infrastructure, which allows to easily write and execute tests. +There are two different kinds of approaches for testing Eclipse SmartHome. One is to use JUnit Plug-in tests with simple JUnit test classes. The other is to extend the test class from the `JavaOSGiTest` class and have the full OSGi environment available to test OSGi services and dynamic behaviour. Both approaches are supported through a simple infrastructure, which allows to easily write and execute tests. Test fragment --- -In OSGi tests are implemented in a separate fragment bundle, which host is the bundle, that should be tested. The name of the test fragment bundle should be the same as the bundle to test with a ".test" suffix. The MANIFEST.MF file must contain a `Fragment-Host` entry. Fragment bundles inherit all imported packages from the host bundle. In addition the fragment bundle must import the `org.junit` package with a minimum version of 4.0.0 specified. The following code snippet shows a manifest file of the test fragment for the `org.eclipse.smarthome.core` bundle. +In general tests are implemented in a separate fragment bundle, which host is the bundle that should be tested. The name of the test fragment bundle should be the same as the bundle to test with a ".test" suffix. The MANIFEST.MF file must contain a `Fragment-Host` entry pointing to the bundle under test. Fragment bundles inherit all imported packages from the host bundle. In addition the fragment bundle must import the `org.junit` package with a minimum version of 4.0.0 specified. The following code snippet shows the MANIFEST.MF file of the test fragment for the `org.eclipse.smarthome.core` bundle. This way all test dependencies are available in the test classes (JUnit, hamcrest, mockito). Manifest-Version: 1.0 Bundle-ManifestVersion: 2 @@ -20,8 +20,23 @@ In OSGi tests are implemented in a separate fragment bundle, which host is the b Bundle-Version: 0.9.0.qualifier Bundle-Vendor: Eclipse.org/SmartHome Fragment-Host: org.eclipse.smarthome.core - Bundle-RequiredExecutionEnvironment: JavaSE-1.7 - Import-Package: org.junit;version="4.0.0" + Bundle-RequiredExecutionEnvironment: JavaSE-1.8 + Import-Package: groovy.lang, + org.codehaus.groovy.reflection, + org.codehaus.groovy.runtime, + org.codehaus.groovy.runtime.callsite, + org.codehaus.groovy.runtime.typehandling, + org.eclipse.smarthome.core.library.items, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.test, + org.eclipse.smarthome.test.java, + org.hamcrest;core=split, + org.junit;version="4.0.0", + org.junit.runner, + org.junit.runners, + org.mockito, + org.osgi.service.cm + Require-Bundle: org.junit,org.mockito,org.hamcrest Tests are typically placed inside the folder `src/test/java`. @@ -43,11 +58,19 @@ Using the the [https://code.google.com/p/hamcrest/ hamcrest] matcher library is PercentType pt = new PercentType("0.0001"); assertThat(pt.toString(), is(equalTo("0.0001"))); -To use the hamcrest library in your test project, you just have to add the following entry to the list of imported packages: - org.hamcrest;core=split +In the OSGi context of Eclipse SmartHome test execution must always be run inside an OSGi runtime. Eclipse PDE Plugin allows to run the JUnit test classes as `Plug-in test` but the required bundles have to be selected first: +The most easy way to execute tests in a test fragment is to use the provided launch configuration from the binding test archetype. This may be modified in the `Run Configurations...` menu to match the required bundles/plug-ins. +Another way is to create a test/package/bundle specific lanuch configuration with the following steps: +- Select the test class/package or bundle +- In context menu select `Run as -> JUnit Plug-in Test` +- after test run (which might fail due to unmet dependencies) select the configuration in `Run Configurations...` +- select `plug-ins selected below only` in the `Plug-ins` tab then `Deselect all` +- search for your test fragment bundle and enable it, then clear the search field (important to enable the action buttons again) +- select `Add Required Plug-ins`, then `Apply`, then `Run` +- since the `Add Required Plug-ins` action is a little overeager it will also select other `.test` fragments which you may be required to deselect manually. -Tests can be executed from Eclipse by right-clicking the test file and clicking on `Run As => JUnit Test`. From maven one can execute the test with `mvn test` command in the folder of the test fragment bundle. +From maven one can execute the test with `mvn install` command from the folder of the test fragment bundle. Mockito --- diff --git a/docs/documentation/features/events.md b/docs/documentation/features/events.md index aecc190fe46..708c73f413d 100644 --- a/docs/documentation/features/events.md +++ b/docs/documentation/features/events.md @@ -74,6 +74,7 @@ The event source is optional and represents the name of the source identifying t | ItemThingLinkRemovedEvent |An item thing link has been removed from the registry. |smarthome/links/{itemName}-{thingUID}/removed | #### Channel Events + | Event |Description |Topic | |-----------------------------|---------------------------------------------------------|------------------------------------------------| | ChannelTriggeredEvent |A channel has been triggered. |smarthome/channels/{channelUID}/triggered | diff --git a/docs/documentation/features/frameworkUtilities.md b/docs/documentation/features/frameworkUtilities.md new file mode 100644 index 00000000000..de9ed2b64a5 --- /dev/null +++ b/docs/documentation/features/frameworkUtilities.md @@ -0,0 +1,14 @@ +--- +layout: documentation +--- + +# Framework Utilities + +In this chapter useful services/utilities of the Eclipse SmartHome project are described. + +## Network Address Service + +The `NetworkAddressService` is an OSGi service that can be used like any other OSGi service by adding a service reference to it. Its OSGi service name is `org.eclipse.smarthome.network`. +A user can configure his default network address via Paper UI under `Configuration -> System -> Network Settings`. +One can obtain the configured address via the `getPrimaryIpv4HostAddress()` method on the service. +This service is useful for example in the `ThingHandlerFactory` or an `AudioSink` where one needs a specific IP address of the host system to provide something like a `callback` URL. \ No newline at end of file diff --git a/docs/documentation/features/icons.md b/docs/documentation/features/icons.md index 6475c81024e..779a225f4e1 100644 --- a/docs/documentation/features/icons.md +++ b/docs/documentation/features/icons.md @@ -12,7 +12,7 @@ Bundle: `org.eclipse.smarthome.ui.icon` Eclipse SmartHome comes with a flexible infrastructure for handling icons that are to be used within user interfaces. This bunde registers a servlet under the url `/icon/`, which can then be easily queried for icons using GET requests of the form `/icon/?state=&format=[png|svg]&iconset=`, where -- `category` is one from the [list of channel categories](../../development/bindings/thing-definition.html#channel-categories) or any custom category that might be used within the solution +- `category` is one from the [list of channel categories](../development/bindings/categories.html#channel-categories) or any custom category that might be used within the solution -`state` (optional) is the string-representation of an item state - `format` (optional) defines the requested format to be either PNG or SVG - `iconset` (optional) specifies an iconset to use diff --git a/docs/documentation/features/internationalization.md b/docs/documentation/features/internationalization.md index 93794a8392f..3dbe073ed58 100644 --- a/docs/documentation/features/internationalization.md +++ b/docs/documentation/features/internationalization.md @@ -132,6 +132,179 @@ channel-type.yahooweather.temperature.option.OPTION2 = Option Nummer 2 So the key for referencing a label of a defined thing type is `thing-type...label`. A label of a channel can be referenced with `channel-type...label`. And finally the config description parameter key is `thing-type.config....label` and the group parameter is `thing-type.config..group...label`. +The following snippet shows an excerpt of the thing type definition XML file of the Weather Underground Binding and its language file that localizes labels and descriptions for the French language. + +XML file (thing-types.xml): + +```xml + + + + + Provides various weather data from the Weather Underground service + + + + + + This is the weather forecast for tomorrow + + + + This is the weather forecast in two days + + + + + + password + + API key to access the Weather Underground service + + + + Multiple syntaxes are supported. Please read the binding documentation for more information + + + + Language to be used by the Weather Underground service + + + + + + + + + Specifies the refresh interval in minutes. + 30 + + + + + + + This is the current weather + + + + + + + + + This is the weather forecast + + + + + + + String + + Weather current conditions + + + + + Number + + Current temperature + Temperature + + + + + Select the temperature unit provided by the Weather Underground service + + + + + C + + + + + + Number + + Maximum temperature + Temperature + + + + + Select the maximum temperature unit provided by the Weather Underground service + + + + + C + + + + + +``` + +Language file (weatherunderground_fr.properties): + +```ini +# binding +binding.weatherunderground.name = Extension WeatherUnderground +binding.weatherunderground.description = L'extension Weather Underground interroge le service Weather Underground pour récupérer des données météo. + +# thing types +thing-type.weatherunderground.weather.label = Informations météo +thing-type.weatherunderground.weather.description = Présente diverses données météo fournies par le service Weather Underground. + +# thing type configuration +thing-type.config.weatherunderground.weather.apikey.label = Clé d'accès +thing-type.config.weatherunderground.weather.apikey.description = La clé d'accès au service Weather Underground. +thing-type.config.weatherunderground.weather.location.label = Emplacement des données météo +thing-type.config.weatherunderground.weather.location.description = Plusieurs syntaxes sont possibles. Merci de consulter la documentation de l'extension pour plus d'information. +thing-type.config.weatherunderground.weather.language.label = Langue +thing-type.config.weatherunderground.weather.language.description = La langue à utiliser par le service Weather Underground. +thing-type.config.weatherunderground.weather.language.option.EN = Anglais +thing-type.config.weatherunderground.weather.language.option.FR = Français +thing-type.config.weatherunderground.weather.language.option.DL = Allemand +thing-type.config.weatherunderground.weather.refresh.label = Fréquence de rafraîchissement +thing-type.config.weatherunderground.weather.refresh.description = La fréquence de rafraîchissement des données en minutes. + +# channel group types +channel-group-type.weatherunderground.current.label = Météo actuelle +channel-group-type.weatherunderground.current.description = La météo actuelle. +channel-group-type.weatherunderground.forecast.label = Météo prévue +channel-group-type.weatherunderground.forecast.description = La météo prévue. + +# channel groups +thing-type.weatherunderground.weather.group.forecastTomorrow.label = Météo de demain +thing-type.weatherunderground.weather.group.forecastTomorrow.description = La météo prévue demain. +thing-type.weatherunderground.weather.group.forecastDay2.label = Météo dans 2 jours +thing-type.weatherunderground.weather.group.forecastDay2.description = La météo prévue dans 2 jours. + +# channel types +channel-type.weatherunderground.currentConditions.label = Conditions actuelles +channel-type.weatherunderground.currentConditions.description = Les conditions météo actuelles. +channel-type.weatherunderground.temperature.label = Température +channel-type.weatherunderground.temperature.description = La température actuelle. +channel-type.weatherunderground.maxTemperature.label = Température maximale +channel-type.weatherunderground.maxTemperature.description = La température maximale. + +# channel type configuration +channel-type.config.weatherunderground.temperature.SourceUnit.label = Unité de température +channel-type.config.weatherunderground.temperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour la température actuelle. +channel-type.config.weatherunderground.temperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.temperature.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.maxTemperature.SourceUnit.label = Unité de température maximale +channel-type.config.weatherunderground.maxTemperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Undergroundde pour la température maximale. +channel-type.config.weatherunderground.maxTemperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.maxTemperature.SourceUnit.option.F = Degrés Fahrenheit + +``` + +So the label of a channel group type can be referenced with `channel-group-type...label` and the label of a channel group definition with `thing-type...group..label`. + ### Using custom Keys In addition to the default keys the developer can also specify custom keys inside the XML file. But with this approach the XML file cannot longer contain the English texts. So it is mandatory to define a language file for the English language. The syntax for custom keys is `@text/`. The keys are unique across the whole bundle, so a constant can reference any key in all files inside the `ESH-INF/i18n` folder. diff --git a/docs/documentation/features/rules.md b/docs/documentation/features/rules.md index 5a19684d2f4..5a20279c8f6 100644 --- a/docs/documentation/features/rules.md +++ b/docs/documentation/features/rules.md @@ -296,9 +296,9 @@ There are several ways to add new rules: * http:///rest/templates" - lists rule templates. * http:///rest/rules - lists rule instances. -#### /rest/ruletemplates - - GET /rest/ruletemplates - returns all registered rule templates. - - GET /rest/ruletemplates/{templateUID} - returned response includes only the content of the specified template. +#### /rest/templates + - GET /rest/templates - returns all registered rule templates. + - GET /rest/templates/{templateUID} - returned response includes only the content of the specified template. #### /rest/module-types - GET /rest/module-types - returns all registered module types. diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.astro.test/META-INF/MANIFEST.MF index 3caf1d633d0..3b4c7dc7ac0 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.astro.test/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: groovy.lang, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.astro/META-INF/MANIFEST.MF index 00fc207a1c1..7dc679b9212 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/META-INF/MANIFEST.MF @@ -10,6 +10,7 @@ Import-Package: com.google.common.collect, org.apache.commons.lang.builder, org.apache.commons.lang.reflect, org.apache.commons.lang.time, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.i18n, diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/README.md b/extensions/binding/org.eclipse.smarthome.binding.astro/README.md index 739e400cdc7..03723523103 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/README.md +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/README.md @@ -20,7 +20,7 @@ No binding configuration required. ## Thing Configuration -All Things require the parameter `geolocation` (as ",,[]") for which the calculation is done. +All Things require the parameter `geolocation` (as `,,[]`) for which the calculation is done. The altitude segment is optional and sharpens results provided by the Radiation group. Optionally, a refresh `interval` (in seconds) can be defined to also calculate positional data like azimuth and elevation. diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/build.properties b/extensions/binding/org.eclipse.smarthome.binding.astro/build.properties index 66e21b90751..e8c3f14cfdf 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/build.properties @@ -3,4 +3,6 @@ output.. = target/classes bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - ESH-INF/ \ No newline at end of file + ESH-INF/,\ + about.html + diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/AstroBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/AstroBindingConstants.java index 24c39da7393..49e13f4b0cd 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/AstroBindingConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/AstroBindingConstants.java @@ -17,11 +17,11 @@ * @author Amit Kumar Mondal - Made non-Instantiable */ public final class AstroBindingConstants { - - /** Constructor */ - private AstroBindingConstants() { - throw new IllegalAccessError("Non-instantiable"); - } + + /** Constructor */ + private AstroBindingConstants() { + throw new IllegalAccessError("Non-instantiable"); + } public static final String BINDING_ID = "astro"; diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobMoon.java b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobMoon.java index 577b415b255..e78510fc18c 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobMoon.java +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobMoon.java @@ -51,23 +51,23 @@ public void run() { return; } Moon moon = (Moon) planet; - scheduleEvent(thingUID, handler, moon.getRise().getStart(), EVENT_START, EVENT_CHANNEL_ID_RISE); - scheduleEvent(thingUID, handler, moon.getSet().getEnd(), EVENT_END, EVENT_CHANNEL_ID_SET); + scheduleEvent(thingUID, handler, moon.getRise().getStart(), EVENT_START, EVENT_CHANNEL_ID_RISE, false); + scheduleEvent(thingUID, handler, moon.getSet().getEnd(), EVENT_END, EVENT_CHANNEL_ID_SET, false); MoonPhase moonPhase = moon.getPhase(); scheduleEvent(thingUID, handler, moonPhase.getFirstQuarter(), EVENT_PHASE_FIRST_QUARTER, - EVENT_CHANNEL_ID_MOON_PHASE); + EVENT_CHANNEL_ID_MOON_PHASE, false); scheduleEvent(thingUID, handler, moonPhase.getThirdQuarter(), EVENT_PHASE_THIRD_QUARTER, - EVENT_CHANNEL_ID_MOON_PHASE); - scheduleEvent(thingUID, handler, moonPhase.getFull(), EVENT_PHASE_FULL, EVENT_CHANNEL_ID_MOON_PHASE); - scheduleEvent(thingUID, handler, moonPhase.getNew(), EVENT_PHASE_NEW, EVENT_CHANNEL_ID_MOON_PHASE); + EVENT_CHANNEL_ID_MOON_PHASE, false); + scheduleEvent(thingUID, handler, moonPhase.getFull(), EVENT_PHASE_FULL, EVENT_CHANNEL_ID_MOON_PHASE, false); + scheduleEvent(thingUID, handler, moonPhase.getNew(), EVENT_PHASE_NEW, EVENT_CHANNEL_ID_MOON_PHASE, false); Eclipse eclipse = moon.getEclipse(); - scheduleEvent(thingUID, handler, eclipse.getPartial(), EVENT_ECLIPSE_PARTIAL, EVENT_CHANNEL_ID_ECLIPSE); - scheduleEvent(thingUID, handler, eclipse.getTotal(), EVENT_ECLIPSE_TOTAL, EVENT_CHANNEL_ID_ECLIPSE); + scheduleEvent(thingUID, handler, eclipse.getPartial(), EVENT_ECLIPSE_PARTIAL, EVENT_CHANNEL_ID_ECLIPSE, false); + scheduleEvent(thingUID, handler, eclipse.getTotal(), EVENT_ECLIPSE_TOTAL, EVENT_CHANNEL_ID_ECLIPSE, false); - scheduleEvent(thingUID, handler, moon.getPerigee().getDate(), EVENT_PERIGEE, EVENT_CHANNEL_ID_PERIGEE); - scheduleEvent(thingUID, handler, moon.getApogee().getDate(), EVENT_APOGEE, EVENT_CHANNEL_ID_APOGEE); + scheduleEvent(thingUID, handler, moon.getPerigee().getDate(), EVENT_PERIGEE, EVENT_CHANNEL_ID_PERIGEE, false); + scheduleEvent(thingUID, handler, moon.getApogee().getDate(), EVENT_APOGEE, EVENT_CHANNEL_ID_APOGEE, false); } @Override diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobSun.java b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobSun.java index 7c4350d5f7d..ef72131d254 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobSun.java +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/DailyJobSun.java @@ -67,9 +67,9 @@ public void run() { scheduleRange(thingUID, handler, sun.getDaylight(), EVENT_CHANNEL_ID_DAYLIGHT); SunEclipse eclipse = sun.getEclipse(); - scheduleEvent(thingUID, handler, eclipse.getPartial(), EVENT_ECLIPSE_PARTIAL, EVENT_CHANNEL_ID_ECLIPSE); - scheduleEvent(thingUID, handler, eclipse.getTotal(), EVENT_ECLIPSE_TOTAL, EVENT_CHANNEL_ID_ECLIPSE); - scheduleEvent(thingUID, handler, eclipse.getRing(), EVENT_ECLIPSE_RING, EVENT_CHANNEL_ID_ECLIPSE); + scheduleEvent(thingUID, handler, eclipse.getPartial(), EVENT_ECLIPSE_PARTIAL, EVENT_CHANNEL_ID_ECLIPSE, false); + scheduleEvent(thingUID, handler, eclipse.getTotal(), EVENT_ECLIPSE_TOTAL, EVENT_CHANNEL_ID_ECLIPSE, false); + scheduleEvent(thingUID, handler, eclipse.getRing(), EVENT_ECLIPSE_RING, EVENT_CHANNEL_ID_ECLIPSE, false); // schedule republish jobs schedulePublishPlanet(thingUID, handler, sun.getZodiac().getEnd()); diff --git a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/Job.java b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/Job.java index 038a97dd87a..84958b2655f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/Job.java +++ b/extensions/binding/org.eclipse.smarthome.binding.astro/src/main/java/org/eclipse/smarthome/binding/astro/internal/job/Job.java @@ -70,8 +70,8 @@ public static void schedule(String thingUID, AstroThingHandler astroHandler, Job * @param channelId the channel ID */ public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt, String event, - String channelId) { - scheduleEvent(thingUID, astroHandler, eventAt, singletonList(event), channelId); + String channelId, boolean configAlreadyApplied) { + scheduleEvent(thingUID, astroHandler, eventAt, singletonList(event), channelId, configAlreadyApplied); } /** @@ -84,7 +84,7 @@ public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler * @param channelId the channel ID */ public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler, Calendar eventAt, - List events, String channelId) { + List events, String channelId, boolean configAlreadyApplied) { boolean thingNull = checkNull(thingUID, "Thing UID is null"); boolean astroHandlerNull = checkNull(astroHandler, "AstroThingHandler is null"); boolean eventAtNull = checkNull(eventAt, "Scheduled Instant is null"); @@ -94,9 +94,14 @@ public static void scheduleEvent(String thingUID, AstroThingHandler astroHandler if (thingNull || astroHandlerNull || eventAtNull || eventsNull || channelIdNull || events.isEmpty()) { return; } - AstroChannelConfig config = astroHandler.getThing().getChannel(channelId).getConfiguration() - .as(AstroChannelConfig.class); - Calendar instant = applyConfig(eventAt, config); + final Calendar instant; + if (!configAlreadyApplied) { + AstroChannelConfig config = astroHandler.getThing().getChannel(channelId).getConfiguration() + .as(AstroChannelConfig.class); + instant = applyConfig(eventAt, config); + } else { + instant = eventAt; + } List jobs = events.stream().map(e -> new EventJob(thingUID, channelId, e)).collect(toList()); schedule(thingUID, astroHandler, new CompositeJob(thingUID, jobs), instant); } @@ -127,11 +132,16 @@ public static void scheduleRange(String thingUID, AstroThingHandler astroHandler return; } - if (truncatedEquals(start, end, SECOND)) { - scheduleEvent(thingUID, astroHandler, start, asList(EVENT_START, EVENT_END), channelId); + AstroChannelConfig config = astroHandler.getThing().getChannel(channelId).getConfiguration() + .as(AstroChannelConfig.class); + Calendar configStart = applyConfig(start, config); + Calendar configEnd = applyConfig(end, config); + + if (truncatedEquals(configStart, configEnd, SECOND)) { + scheduleEvent(thingUID, astroHandler, configStart, asList(EVENT_START, EVENT_END), channelId, true); } else { - scheduleEvent(thingUID, astroHandler, start, EVENT_START, channelId); - scheduleEvent(thingUID, astroHandler, end, EVENT_END, channelId); + scheduleEvent(thingUID, astroHandler, configStart, EVENT_START, channelId, true); + scheduleEvent(thingUID, astroHandler, configEnd, EVENT_END, channelId, true); } } @@ -197,7 +207,7 @@ public static void scheduleSunPhase(String thingUID, AstroThingHandler astroHand */ public static boolean checkNull(T obj, String message) { if (isNull(obj)) { - logger.trace(message); + logger.trace("{}", message); return true; } return false; diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/META-INF/MANIFEST.MF index 388457affb5..8cb7ffc7b5d 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/META-INF/MANIFEST.MF @@ -13,6 +13,7 @@ Import-Package: com.google.common.collect, javax.xml.bind, org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.common, diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/BridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/BridgeHandler.java index 3d912e19550..b9695829188 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/BridgeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/BridgeHandler.java @@ -137,7 +137,7 @@ public void run() { if (connMan.getApplicationToken() != null) { configuration.remove(USER_NAME); configuration.remove(PASSWORD); - logger.debug("Application-Token is: " + connMan.getApplicationToken()); + logger.debug("Application-Token is: {}", connMan.getApplicationToken()); configuration.put(APPLICATION_TOKEN, connMan.getApplicationToken()); configChanged = true; } @@ -172,7 +172,7 @@ public void initialize() { Config config = loadAndCheckConfig(); if (config != null) { - logger.debug(config.toString()); + logger.debug("{}", config.toString()); scheduler.execute(new Initializer(this, config)); } } @@ -654,7 +654,8 @@ public void onStatusChanged(ManagerTypes managerType, ManagerStates state) { break; case STOPPED: if (!getThing().getStatusInfo().getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR) - && !getThing().getStatusInfo().getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)) { + && !getThing().getStatusInfo().getStatusDetail() + .equals(ThingStatusDetail.CONFIGURATION_ERROR)) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "DeviceStatusManager is stopped."); } break; diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/DeviceHandler.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/DeviceHandler.java index ccc9c6fca2d..b5c97bf5441 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/DeviceHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/DeviceHandler.java @@ -230,7 +230,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { device.setOutputValue(Short.parseShort(((StringType) command).toString())); } } else { - logger.warn("Command sent to an unknown channel id: " + channelUID); + logger.warn("Command sent to an unknown channel id: {}", channelUID); } } else { if (channelUID.getId().equals(CHANNEL_ID_SHADE_ANGLE)) { @@ -274,7 +274,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } } else { - logger.warn("Command sent to an unknown channel id: " + channelUID); + logger.warn("Command sent to an unknown channel id: {}", channelUID); } } } @@ -587,9 +587,9 @@ private void loadSensorChannels(Configuration config) { } device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio); - logger.debug("add sensor prioritys: active power = " + activePowerPrio + ", output current = " - + outputCurrentPrio + ", electric meter = " + electricMeterPrio + " to device with id " - + device.getDSID()); + logger.debug( + "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}", + activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID()); // check and load sensor channels of the thing checkSensorChannel(activePowerPrio, outputCurrentPrio, electricMeterPrio); diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/SceneHandler.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/SceneHandler.java index 4ae6b81b859..78866f713ef 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/SceneHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/handler/SceneHandler.java @@ -275,7 +275,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } } else { - logger.warn("Command sent to an unknown channel id: " + channelUID); + logger.warn("Command sent to an unknown channel id: {}", channelUID); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java index 4e24f849a7c..20e111100e9 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java @@ -59,13 +59,13 @@ public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, if (BridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { ThingUID dSSUID = getBridgeThingUID(thingTypeUID, thingUID, configuration); if (dSSUID != null) { - logger.info(dSSUID.toString()); + logger.info("{}", dSSUID); return super.createThing(thingTypeUID, configuration, dSSUID, null); } else { - logger.error("Can't generate thing UID for thing type " + thingTypeUID + logger.error("Can't generate thing UID for thing type {}" + ", because digitalSTROM-Server is not reachable. Please check these points:\n" + "Are the server address and portnumber correct?\n" + "Is the server turned on?\n" - + "Is the network configured correctly?"); + + "Is the network configured correctly?", thingTypeUID); return null; } } @@ -130,16 +130,17 @@ private ThingUID getSceneUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Confi String sceneID = SceneHandler.getSceneID(configuration, bridgeHandlers.get(bridgeUID)); switch (sceneID) { case SceneHandler.SCENE_WRONG: - logger.error("Configured scene '" + configuration.get(DigitalSTROMBindingConstants.SCENE_ID) - + "' does not exist or can not be used, please check your configuration."); + logger.error( + "Configured scene '{}' does not exist or can not be used, please check your configuration.", + configuration.get(DigitalSTROMBindingConstants.SCENE_ID)); break; case SceneHandler.ZONE_WRONG: - logger.error("Configured zone '" + configuration.get(DigitalSTROMBindingConstants.SCENE_ZONE_ID) - + "' does not exist, please check your configuration."); + logger.error("Configured zone '{}' does not exist, please check your configuration.", + configuration.get(DigitalSTROMBindingConstants.SCENE_ZONE_ID)); break; case SceneHandler.GROUP_WRONG: - logger.error("Configured group '" + configuration.get(DigitalSTROMBindingConstants.SCENE_GROUP_ID) - + "' does not exist, please check your configuration."); + logger.error("Configured group '{}' does not exist, please check your configuration.", + configuration.get(DigitalSTROMBindingConstants.SCENE_GROUP_ID)); break; case SceneHandler.NO_STRUC_MAN: logger.error("Waiting for building digitalSTROM model."); diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java index 57afb48ac20..b5e13b6c47b 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java @@ -65,8 +65,8 @@ public DeviceDiscoveryService(BridgeHandler bridgeHandler, ThingTypeUID supporte */ @Override public void deactivate() { - logger.debug("deactivate discovery service for device type " + deviceType + " thing types are: " - + super.getSupportedThingTypes().toString()); + logger.debug("deactivate discovery service for device type {} thing types are: {}", deviceType, + super.getSupportedThingTypes().toString()); removeOlderResults(new Date().getTime()); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java index 32999eb284c..979c29cbbfd 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/discovery/SceneDiscoveryService.java @@ -62,8 +62,8 @@ public SceneDiscoveryService(BridgeHandler bridgeHandler, ThingTypeUID supported */ @Override public void deactivate() { - logger.debug("deactivate discovery service for scene type " + sceneType + " remove thing tyspes " - + super.getSupportedThingTypes().toString()); + logger.debug("deactivate discovery service for scene type {} remove thing tyspes {}", sceneType, + super.getSupportedThingTypes()); removeOlderResults(new Date().getTime()); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java index d5e620028c1..1fd1690ba75 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/DeviceStatusManagerImpl.java @@ -198,8 +198,8 @@ public void run() { if (trashDevices.isEmpty()) { currentDevice.setConfig(config); strucMan.addDeviceToStructure(currentDevice); - logger.debug("trashDevices are empty, add Device with dSID " - + currentDevice.getDSID().toString() + " to the deviceMap!"); + logger.debug("trashDevices are empty, add Device with dSID {} to the deviceMap!", + currentDevice.getDSID()); } else { logger.debug("Search device in trashDevices."); TrashDevice foundTrashDevice = null; @@ -246,7 +246,7 @@ public void run() { if (!sceneMan.scenesGenerated() && !sceneMan.getManagerState().equals(ManagerStates.GENERATING_SCENES)) { - logger.debug(sceneMan.getManagerState().toString()); + logger.debug("{}", sceneMan.getManagerState()); sceneMan.generateScenes(); } @@ -276,7 +276,7 @@ public void run() { if (trashDevice.isTimeToDelete(Calendar.getInstance().get(Calendar.DAY_OF_YEAR))) { logger.debug("Found trashDevice that have to delete!"); trashDevices.remove(trashDevice); - logger.debug("Delete trashDevice: " + trashDevice.getDevice().getDSID().getValue()); + logger.debug("Delete trashDevice: {}", trashDevice.getDevice().getDSID().getValue()); } } lastBinCheck = System.currentTimeMillis(); @@ -359,7 +359,7 @@ private synchronized void stateChanged(ManagerStates state) { public synchronized void start() { logger.debug("start pollingScheduler"); if (pollingScheduler == null || pollingScheduler.isCancelled()) { - pollingScheduler = scheduler.scheduleAtFixedRate(new PollingRunnable(), 0, config.getPollingFrequency(), + pollingScheduler = scheduler.scheduleWithFixedDelay(new PollingRunnable(), 0, config.getPollingFrequency(), TimeUnit.MILLISECONDS); sceneMan.start(); } @@ -485,8 +485,8 @@ public synchronized void sendSceneComandsToDSS(InternalScene scene, boolean call boolean requestSuccsessfull = false; if (scene.getZoneID() == 0) { if (call_undo) { - logger.debug(scene.getGroupID() + " " + scene.getSceneID() + " " - + ApartmentSceneEnum.getApartmentScene(scene.getSceneID())); + logger.debug("{} {} {}", scene.getGroupID(), scene.getSceneID(), + ApartmentSceneEnum.getApartmentScene(scene.getSceneID())); requestSuccsessfull = this.digitalSTROMClient.callApartmentScene(connMan.getSessionToken(), scene.getGroupID(), null, ApartmentSceneEnum.getApartmentScene(scene.getSceneID()), false); @@ -506,7 +506,7 @@ public synchronized void sendSceneComandsToDSS(InternalScene scene, boolean call } } - logger.debug("Was the scene call succsessful?: " + requestSuccsessfull); + logger.debug("Was the scene call succsessful?: {}", requestSuccsessfull); if (requestSuccsessfull) { this.sceneMan.addEcho(scene.getID()); if (call_undo) { @@ -922,7 +922,7 @@ public void updateSceneData(Device device, DeviceStateUpdate deviceStateUpdate) public void registerDeviceListener(DeviceStatusListener deviceListener) { if (deviceListener != null) { String id = deviceListener.getDeviceStatusListenerID(); - logger.debug("register DeviceListener with id: " + id); + logger.debug("register DeviceListener with id: {}", id); if (id.equals(DeviceStatusListener.DEVICE_DISCOVERY)) { this.deviceDiscovery = deviceListener; for (Device device : strucMan.getDeviceMap().values()) { diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/SceneManagerImpl.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/SceneManagerImpl.java index 797b83587ff..9f0043c166b 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/SceneManagerImpl.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/manager/impl/SceneManagerImpl.java @@ -112,7 +112,7 @@ public void handleEvent(EventItem eventItem) { try { sceneId = Short.parseShort(sceneStr); } catch (java.lang.NumberFormatException e) { - logger.error("An exception occurred, while handling event at parsing sceneID: " + sceneStr, e); + logger.error("An exception occurred, while handling event at parsing sceneID: {}", sceneStr, e); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/AbstractSensorJobExecutor.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/AbstractSensorJobExecutor.java index 902a1acb26c..5a265875d09 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/AbstractSensorJobExecutor.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/AbstractSensorJobExecutor.java @@ -67,7 +67,7 @@ public void run() { sensorJob.execute(dSAPI, connectionManager.getSessionToken()); } if (circuit.noMoreJobs()) { - logger.debug("no more jobs... stop circuit schedduler with id = " + circuit.getMeterDSID().toString()); + logger.debug("no more jobs... stop circuit schedduler with id = {}", circuit.getMeterDSID()); pollingSchedulers.get(circuit.getMeterDSID()).cancel(true); } } @@ -112,7 +112,7 @@ private void startSchedduler(CircuitScheduler circuit) { if (pollingSchedulers.get(circuit.getMeterDSID()) == null || pollingSchedulers.get(circuit.getMeterDSID()).isCancelled()) { pollingSchedulers.put(circuit.getMeterDSID(), - scheduler.scheduleAtFixedRate(new ExecutorRunnable(circuit), circuit.getNextExecutionDelay(), + scheduler.scheduleWithFixedDelay(new ExecutorRunnable(circuit), circuit.getNextExecutionDelay(), config.getSensorReadingWaitTime(), TimeUnit.MILLISECONDS)); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/CircuitScheduler.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/CircuitScheduler.java index 44b5b04729f..4b8d66f3fbf 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/CircuitScheduler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/CircuitScheduler.java @@ -66,8 +66,8 @@ public CircuitScheduler(SensorJob sensorJob, Config config) { this.meterDSID = sensorJob.getMeterDSID(); this.sensorJobQueue.add(sensorJob); this.config = config; - logger.debug("create circuitScheduler: " + this.getMeterDSID() + " and add sensorJob: " - + sensorJob.getDSID().toString()); + logger.debug("create circuitScheduler: {} and add sensorJob: {}", this.getMeterDSID(), + sensorJob.getDSID().toString()); } /** @@ -88,12 +88,12 @@ public void addSensorJob(SensorJob sensorJob) { synchronized (sensorJobQueue) { if (!this.sensorJobQueue.contains(sensorJob)) { sensorJobQueue.add(sensorJob); - logger.debug("Add sensorJob: " + sensorJob.toString() + "to circuitScheduler: " + this.getMeterDSID()); + logger.debug("Add sensorJob: {} to circuitScheduler: {}", sensorJob.toString(), this.getMeterDSID()); } else if (checkSensorJobPrio(sensorJob)) { - logger.debug("add sensorJob: " + sensorJob.toString() + "with higher priority to circuitScheduler: " - + this.getMeterDSID()); + logger.debug("add sensorJob: {} with higher priority to circuitScheduler: {}", sensorJob.toString(), + this.getMeterDSID()); } else { - logger.debug("sensorJob: " + sensorJob.getDSID().toString() + " allready exist with a higher priority"); + logger.debug("sensorJob: {} allready exist with a higher priority", sensorJob.getDSID()); } } } @@ -164,7 +164,7 @@ public void removeSensorJob(DSID dSID) { iter.remove(); } } - logger.debug("Remove SensorJobs from device with dSID {}." + dSID); + logger.debug("Remove SensorJobs from device with dSID {}.", dSID); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/DeviceOutputValueSensorJob.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/DeviceOutputValueSensorJob.java index 141d97929eb..5b2f8ada30c 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/DeviceOutputValueSensorJob.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/DeviceOutputValueSensorJob.java @@ -51,7 +51,7 @@ public DeviceOutputValueSensorJob(Device device) { @Override public void execute(DsAPI digitalSTROM, String token) { int value = digitalSTROM.getDeviceOutputValue(token, this.device.getDSID(), null, index); - logger.debug("Device output value on Demand : " + value + ", dSID: " + this.device.getDSID().getValue()); + logger.debug("Device output value on Demand : {}, dSID: {}", value, this.device.getDSID().getValue()); if (value != 1) { switch (this.index) { @@ -65,8 +65,8 @@ public void execute(DsAPI digitalSTROM, String token) { if (device.isBlind()) { value = digitalSTROM.getDeviceOutputValue(token, this.device.getDSID(), null, DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT); - logger.debug("Device angle output value on Demand : " + value + ", dSID: " - + this.device.getDSID().getValue()); + logger.debug("Device angle output value on Demand : {}, dSID: {}", value, + this.device.getDSID().getValue()); if (value != 1) { this.device.updateInternalDeviceState( new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SLAT_ANGLE, value)); diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneConfigReadingJob.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneConfigReadingJob.java index 4880d2e8407..7d5fb85957e 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneConfigReadingJob.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneConfigReadingJob.java @@ -50,8 +50,8 @@ public void execute(DsAPI digitalSTROM, String token) { if (sceneConfig != null) { device.addSceneConfig(sceneID, sceneConfig); - logger.debug("UPDATED scene configuration for dSID: " + this.device.getDSID() + ", sceneID: " + sceneID - + ", configuration: " + sceneConfig.toString()); + logger.debug("UPDATED scene configuration for dSID: {}, sceneID: {}, configuration: {}", + this.device.getDSID(), sceneID, sceneConfig); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneOutputValueReadingJob.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneOutputValueReadingJob.java index 08c6f05d32c..0010cc49cc0 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneOutputValueReadingJob.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/sensorJobExecutor/sensorJob/impl/SceneOutputValueReadingJob.java @@ -53,8 +53,8 @@ public void execute(DsAPI digitalSTROM, String token) { } else { device.setSceneOutputValue(this.sceneID, sceneValue[0]); } - logger.debug("UPDATED sceneOutputValue for dsid: " + this.device.getDSID() + ", sceneID: " + sceneID - + ", value: " + sceneValue[0] + ", angle: " + sceneValue[1]); + logger.debug("UPDATED sceneOutputValue for dsid: {}, sceneID: {}, value: {}, angle: {}", + this.device.getDSID(), sceneID, sceneValue[0], sceneValue[1]); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/HttpTransportImpl.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/HttpTransportImpl.java index 0c1837ce6b7..be9b1f42d9f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/HttpTransportImpl.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/HttpTransportImpl.java @@ -49,10 +49,10 @@ * {@link Config#getCert()}. If there is no SSL-Certificate, but an path to an external SSL-Certificate file what is set * in {@link Config#getTrustCertPath()} this will be set. If no SSL-Certificate is set in the {@link Config} it will be * red out from the server and set in {@link Config#setCert(String)}. - * + * *

    * If no {@link Config} is given the SSL-Certificate will be stored locally. - * + * *

    * The method {@link #writePEMCertFile(String)} saves the SSL-Certificate in a file at the given path. If all * SSL-Certificates shout be ignored the flag exeptAllCerts have to be true at the constructor @@ -262,7 +262,7 @@ public int checkConnection(String testRequest) { if (e instanceof java.net.UnknownHostException) { return -5; } - logger.error("An IOException occurred by executing jsonRequest: " + testRequest, e); + logger.error("An IOException occurred by executing jsonRequest: {}", testRequest, e); } return -1; } @@ -290,11 +290,11 @@ private String readPEMCertificateStringFromFile(String path) { if (cert.startsWith(BEGIN_CERT)) { return cert; } else { - logger.error( - "File is not a PEM certificate file. PEM-Certificats starts with: " + BEGIN_CERT); + logger.error("File is not a PEM certificate file. PEM-Certificats starts with: {}", + BEGIN_CERT); } } catch (FileNotFoundException e) { - logger.error("Can't find a certificate file at the path: " + path + "\nPlease check the path!"); + logger.error("Can't find a certificate file at the path: {}\nPlease check the path!", path); } catch (IOException e) { logger.error("An IOException occurred: ", e); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/JSONResponseHandler.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/JSONResponseHandler.java index 91f3bee1c64..ce65f2cfd03 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/JSONResponseHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/serverConnection/impl/JSONResponseHandler.java @@ -40,8 +40,8 @@ public static boolean checkResponse(JsonObject jsonResponse) { return jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_OK.getKey()).toString() .equals(JSONApiResponseKeysEnum.RESPONSE_SUCCESSFUL.getKey()); } else { - logger.error("JSONResponseHandler: error in json request. Error message : " - + jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_MESSAGE.getKey()).toString()); + logger.error("JSONResponseHandler: error in json request. Error message : {}", + jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_MESSAGE.getKey())); } return false; } @@ -58,7 +58,7 @@ public static JsonObject toJsonObject(String jsonResponse) { try { return (JsonObject) new JsonParser().parse(jsonResponse); } catch (JsonParseException e) { - logger.error("An JsonParseException occurred by parsing jsonRequest: " + jsonResponse, e); + logger.error("An JsonParseException occurred by parsing jsonRequest: {}", jsonResponse, e); } } return null; diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/devices/impl/JSONDeviceImpl.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/devices/impl/JSONDeviceImpl.java index c2c0836b951..db5675e2ad7 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/devices/impl/JSONDeviceImpl.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/devices/impl/JSONDeviceImpl.java @@ -653,7 +653,7 @@ public synchronized void undoInternalScene(InternalScene scene) { activeScene.activateSceneByDevice(); } else { internalUndoScene(); - logger.debug("internalUndo Scene dSID " + dsid.getValue()); + logger.debug("internalUndo Scene dSID {}", dsid.getValue()); this.activeScene = null; } lastCallWasUndo = true; @@ -1319,8 +1319,8 @@ private void setCachedMeterData() { */ private void informListenerAboutStateUpdate(DeviceStateUpdate deviceStateUpdate) { if (listener != null) { - logger.debug("Inform listener about device state changed: type: " + deviceStateUpdate.getType() - + ", value: " + deviceStateUpdate.getValue()); + logger.debug("Inform listener about device state changed: type: {}, value: {}", deviceStateUpdate.getType(), + deviceStateUpdate.getValue()); listener.onDeviceStateChanged(deviceStateUpdate); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java index a16f7e8078f..36608b2d193 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/SceneDiscovery.java @@ -235,7 +235,7 @@ public void generateReachableScenes(final ConnectionManager connectionManager, final StructureManager structureManager) { if (generateReachableScenesScheduledFuture == null || generateReachableScenesScheduledFuture.isCancelled()) { generateReachableScenesScheduledFuture = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME) - .scheduleAtFixedRate(new Runnable() { + .scheduleWithFixedDelay(new Runnable() { HashMap> reachableGroups = getReachableGroups(connectionManager); Iterator zoneIdInter = null; @@ -391,15 +391,16 @@ public void sceneDiscoverd(InternalScene scene) { if (!isStandardScene(scene.getSceneID())) { if (this.discovery != null) { this.discovery.onSceneAdded(scene); - logger.debug("Inform scene discovery about added scene with id: " + scene.getID()); + logger.debug("Inform scene discovery about added scene with id: {}", scene.getID()); } else { - logger.debug("Can't inform scene discovery about added scene with id: " + scene.getID() - + " because scene discovery is disabled"); + logger.debug( + "Can't inform scene discovery about added scene with id: {} because scene discovery is disabled", + scene.getID()); } } this.sceneManager.addInternalScene(scene); } else { - logger.error("Added scene with id: " + scene.getID() + " is a not usage scene!"); + logger.error("Added scene with id: {} is a not usage scene!", scene.getID()); } } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/sceneEvent/EventListener.java b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/sceneEvent/EventListener.java index a63fd8ed53e..4a8dab01164 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/sceneEvent/EventListener.java +++ b/extensions/binding/org.eclipse.smarthome.binding.digitalstrom/src/main/java/org/eclipse/smarthome/binding/digitalstrom/internal/lib/structure/scene/sceneEvent/EventListener.java @@ -81,7 +81,7 @@ public synchronized void stop() { */ public synchronized void start() { if (subscribe() && (pollingScheduler == null || pollingScheduler.isCancelled())) { - pollingScheduler = scheduler.scheduleAtFixedRate(runableListener, 0, + pollingScheduler = scheduler.scheduleWithFixedDelay(runableListener, 0, config.getEventListenerRefreshinterval(), TimeUnit.MICROSECONDS); logger.debug("Start EventListener"); } @@ -137,7 +137,7 @@ public void run() { subscribe(); } else if (errorStr != null) { pollingScheduler.cancel(true); - logger.error("Unknown error message at event response: " + errorStr); + logger.error("Unknown error message at event response: {}", errorStr); } } } @@ -169,7 +169,7 @@ private void handleEvent(JsonArray array) { if (array.size() > 0) { Event event = new JSONEventImpl(array); for (EventItem item : event.getEventItems()) { - logger.info(item.getProperties().toString()); + logger.info("{}", item.getProperties()); this.sceneManager.handleEvent(item); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/META-INF/MANIFEST.MF index 3c41b6c44c6..ced18999e60 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Bundle-Vendor: Eclipse.org/SmartHome Fragment-Host: org.eclipse.smarthome.binding.fsinternetradio Import-Package: javax.servlet, javax.servlet.http, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.items, diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/about.html b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/build.properties b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/build.properties index cbd581a6464..a17b084ad78 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/build.properties @@ -1,5 +1,6 @@ source.. = src/test/java/ bin.includes = META-INF/,\ - . + .,\ + about.html output.. = bin/,\ target/test-classes/ diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerOSGiTest.java b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerOSGiTest.java index 9c01aef6d76..b1532ded85d 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerOSGiTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerOSGiTest.java @@ -835,7 +835,9 @@ private Item initializeItem(ChannelUID channelUID, String itemName, String accep break; } - itemRegistry.add(item); + if (item != null) { + itemRegistry.add(item); + } itemChannelLinkProvider.add(new ItemChannelLink(itemName, channelUID)); try { Thread.sleep(WAIT_ITEM_CHANNEL_LINK_TO_BE_ADDED); diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/META-INF/MANIFEST.MF index fea69866061..679455de471 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ClassPath: . Import-Package: org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.jetty.client, org.eclipse.jetty.client.api, org.eclipse.jetty.client.util, diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/handler/FSInternetRadioHandler.java b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/handler/FSInternetRadioHandler.java index fba307bd6c5..7a40938dd72 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/handler/FSInternetRadioHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/handler/FSInternetRadioHandler.java @@ -98,7 +98,7 @@ public void run() { updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoText())); break; default: - logger.warn("Ignoring unknown channel during update: " + channel.getLabel()); + logger.warn("Ignoring unknown channel during update: {}", channel.getLabel()); } } } @@ -176,7 +176,7 @@ public void dispose() { public void handleCommand(final ChannelUID channelUID, final Command command) { if (!radio.isLoggedIn()) { // connection to radio is not initialized, log ignored command and set status, if it is not already offline - logger.debug("Ignoring command " + channelUID.getId() + " = " + command + " because device is offline."); + logger.debug("Ignoring command {} = {} because device is offline.", channelUID.getId(), command); if (ThingStatus.ONLINE.equals(getThing().getStatus())) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); } @@ -231,7 +231,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { } break; default: - logger.warn("Ignoring unknown command: " + command); + logger.warn("Ignoring unknown command: {}", command); } // make sure that device state is online updateStatus(ThingStatus.ONLINE); diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/internal/radio/FrontierSiliconRadioApiResult.java b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/internal/radio/FrontierSiliconRadioApiResult.java index ce8bd303203..a8ad3ffb977 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/internal/radio/FrontierSiliconRadioApiResult.java +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio/src/main/java/org/eclipse/smarthome/binding/fsinternetradio/internal/radio/FrontierSiliconRadioApiResult.java @@ -30,7 +30,7 @@ * *
      * 
    - * 	<fsapiResponse> <status>FS_OK</status> <value><u8>1</u8></value> </fsapiResponse>
    + *   <fsapiResponse> <status>FS_OK</status> <value><u8>1</u8></value> </fsapiResponse>
      * 
      * 
    * @@ -61,9 +61,9 @@ public FrontierSiliconRadioApiResult(String requestResultString) throws IOExcept try { xml = getXmlDocFromString(requestResultString); } catch (Exception e) { - logger.trace("converting to XML failed: '" + requestResultString + "' with " + e.getClass().getName() + ": " - + e.getMessage()); - logger.debug("converting to XML failed with " + e.getClass().getName() + ": " + e.getMessage()); + logger.trace("converting to XML failed: '{}' with {}: {}", requestResultString, e.getClass().getName(), + e.getMessage()); + logger.debug("converting to XML failed with {}: {}", e.getClass().getName(), e.getMessage()); if (e instanceof IOException) { throw (IOException) e; } @@ -82,7 +82,7 @@ private String getStatus() { final Element statusNode = (Element) fsApiResult.getElementsByTagName("status").item(0); final String status = getCharacterDataFromElement(statusNode); - logger.trace("status is: " + status); + logger.trace("status is: {}", status); return status; } @@ -108,11 +108,11 @@ public boolean getValueU8AsBoolean() { final Element u8Node = (Element) valueNode.getElementsByTagName("u8").item(0); final String value = getCharacterDataFromElement(u8Node); - logger.trace("value is: " + value); + logger.trace("value is: {}", value); return "1".equals(value); } catch (Exception e) { - logger.error("getting Value.U8 failed with " + e.getClass().getName() + ": " + e.getMessage()); + logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage()); return false; } } @@ -129,11 +129,11 @@ public int getValueU8AsInt() { final Element u8Node = (Element) valueNode.getElementsByTagName("u8").item(0); final String value = getCharacterDataFromElement(u8Node); - logger.trace("value is: " + value); + logger.trace("value is: {}", value); return Integer.parseInt(value); } catch (Exception e) { - logger.error("getting Value.U8 failed with " + e.getClass().getName() + ": " + e.getMessage()); + logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage()); return 0; } } @@ -150,11 +150,11 @@ public int getValueU32AsInt() { final Element u32Node = (Element) valueNode.getElementsByTagName("u32").item(0); final String value = getCharacterDataFromElement(u32Node); - logger.trace("value is: " + value); + logger.trace("value is: {}", value); return Integer.parseInt(value); } catch (Exception e) { - logger.error("getting Value.U32 failed with " + e.getClass().getName() + ": " + e.getMessage()); + logger.error("getting Value.U32 failed with {}: {}", e.getClass().getName(), e.getMessage()); return 0; } } @@ -171,11 +171,11 @@ public String getValueC8ArrayAsString() { final Element c8Array = (Element) valueNode.getElementsByTagName("c8_array").item(0); final String value = getCharacterDataFromElement(c8Array); - logger.trace("value is: " + value); + logger.trace("value is: {}", value); return value; } catch (Exception e) { - logger.error("getting Value.c8array failed with " + e.getClass().getName() + ": " + e.getMessage()); + logger.error("getting Value.c8array failed with {}: {}", e.getClass().getName(), e.getMessage()); return ""; } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.hue.test/META-INF/MANIFEST.MF index 85c092b503c..bef76dd9d8f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Import-Package: com.google.gson, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.jetty.client, org.eclipse.smarthome.config.discovery.inbox, org.eclipse.smarthome.core.common.registry, diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/org.eclipse.smarthome.binding.hue.test.launch b/extensions/binding/org.eclipse.smarthome.binding.hue.test/org.eclipse.smarthome.binding.hue.test.launch index 03475677f3e..69e6eabadab 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/org.eclipse.smarthome.binding.hue.test.launch +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/org.eclipse.smarthome.binding.hue.test.launch @@ -26,7 +26,7 @@ - + @@ -34,7 +34,7 @@ - + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueBridgeHandlerOSGiTest.groovy b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueBridgeHandlerOSGiTest.groovy index 62b70dfc214..e37b19b5ce5 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueBridgeHandlerOSGiTest.groovy +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueBridgeHandlerOSGiTest.groovy @@ -65,13 +65,13 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - HueBridge hueBridge = new HueBridge(DUMMY_HOST) { + hueBridgeHandler.hueBridge = new HueBridge(DUMMY_HOST) { String link(String deviceType) throws IOException, ApiException { return TEST_USER_NAME; }; } - hueBridgeHandler.onNotAuthenticated(hueBridge) + hueBridgeHandler.onNotAuthenticated() assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME)) } @@ -90,11 +90,11 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - HueBridge hueBridge = new HueBridge(DUMMY_HOST) { + hueBridgeHandler.hueBridge = new HueBridge(DUMMY_HOST) { void authenticate(String userName) throws IOException, ApiException {}; } - hueBridgeHandler.onNotAuthenticated(hueBridge) + hueBridgeHandler.onNotAuthenticated() assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME)) } @@ -113,13 +113,13 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - HueBridge hueBridge = new HueBridge(DUMMY_HOST) { + hueBridgeHandler.hueBridge = new HueBridge(DUMMY_HOST) { void authenticate(String userName) throws IOException, ApiException { throw new UnauthorizedException() }; } - hueBridgeHandler.onNotAuthenticated(hueBridge) + hueBridgeHandler.onNotAuthenticated() assertThat(bridge.getConfiguration().get(USER_NAME), equalTo("notAuthenticatedUser")) assertThat(bridge.getStatus(), equalTo(ThingStatus.OFFLINE)) @@ -139,13 +139,13 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - HueBridge hueBridge = new HueBridge(DUMMY_HOST) { + hueBridgeHandler.hueBridge = new HueBridge(DUMMY_HOST) { String link(String deviceType) throws IOException, ApiException { throw new LinkButtonException() }; } - hueBridgeHandler.onNotAuthenticated(hueBridge) + hueBridgeHandler.onNotAuthenticated() assertThat(bridge.getConfiguration().get(USER_NAME), is(nullValue())) assertThat(bridge.getStatus(), equalTo(ThingStatus.OFFLINE)) @@ -165,13 +165,13 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - HueBridge hueBridge = new HueBridge(DUMMY_HOST) { + hueBridgeHandler.hueBridge = new HueBridge(DUMMY_HOST) { String link(String deviceType) throws IOException, ApiException { throw new ApiException() }; } - hueBridgeHandler.onNotAuthenticated(hueBridge) + hueBridgeHandler.onNotAuthenticated() assertThat(bridge.getConfiguration().get(USER_NAME), is(nullValue())) assertThat(bridge.getStatus(), equalTo(ThingStatus.OFFLINE)) @@ -190,14 +190,14 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class); hueBridgeHandler.thingUpdated(bridge) - hueBridgeHandler.onConnectionLost(hueBridgeHandler.bridge) + hueBridgeHandler.onConnectionLost() assertThat(bridge.getStatus(), is(ThingStatus.OFFLINE)) assertThat(bridge.getStatusInfo().getStatusDetail(), is(not(ThingStatusDetail.BRIDGE_OFFLINE))) } @Test - void 'assert that a status configuration message for missing bridge IP is properly returned (IP is null)'() { + void 'assert that a status configuration message for missing bridge IP is properly returned - IP is null'() { Configuration configuration = new Configuration().with { put(HOST, null) put(SERIAL_NUMBER, "testSerialNumber") @@ -218,7 +218,7 @@ class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTest { } @Test - void 'assert that a status configuration message for missing bridge IP is properly returned (IP is an empty string)'() { + void 'assert that a status configuration message for missing bridge IP is properly returned - IP is an empty string'() { Configuration configuration = new Configuration().with { put(HOST, "") put(SERIAL_NUMBER, "testSerialNumber") diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueLightHandlerOSGiTest.groovy b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueLightHandlerOSGiTest.groovy index c0f315cac91..fd85ba48767 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueLightHandlerOSGiTest.groovy +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/groovy/org/eclipse/smarthome/binding/hue/test/HueLightHandlerOSGiTest.groovy @@ -64,6 +64,8 @@ class HueLightHandlerOSGiTest extends AbstractHueOSGiTest { final String OSRAM_MODEL_TYPE = "PAR16 50 TW" final String OSRAM_MODEL_TYPE_ID = "PAR16_50_TW" + def UNIQUE_ID = "00:17:88:01:00:e1:88:29-0b" + ThingRegistry thingRegistry ItemChannelLinkRegistry linkRegistry ItemRegistry itemRegistry @@ -136,24 +138,7 @@ class HueLightHandlerOSGiTest extends AbstractHueOSGiTest { assertThat hueLightHandler, is(notNullValue()) } - def AsyncResultWrapper addressWrapper = new AsyncResultWrapper() - def AsyncResultWrapper bodyWrapper = new AsyncResultWrapper() - - MockedHttpClient mockedHttpClient = [ - put: { String address, String body -> - addressWrapper.set(address) - bodyWrapper.set(body) - new Result("", 200) - }, - get: { String address -> - if (address.endsWith("testUserName/")) { - new Result(new HueLightState().toString(), 200) - } - } - ] as MockedHttpClient - HueBridgeHandler hueBridgeHandler = hueLightHandler.getHueBridgeHandler() - installHttpClientMock(hueBridgeHandler, mockedHttpClient) assertBridgeOnline(hueLightHandler.getBridge()) hueLightHandler.initialize() @@ -162,7 +147,7 @@ class HueLightHandlerOSGiTest extends AbstractHueOSGiTest { assertThat(hueLight.getStatus(), is(ThingStatus.ONLINE)) }, 10000) - hueBridgeHandler.onConnectionLost(hueBridgeHandler.bridge) + hueBridgeHandler.onConnectionLost() assertThat(hueBridge.getStatus(), is(ThingStatus.OFFLINE)) assertThat(hueBridge.getStatusInfo().getStatusDetail(), is(not(ThingStatusDetail.BRIDGE_OFFLINE))) @@ -516,8 +501,8 @@ class HueLightHandlerOSGiTest extends AbstractHueOSGiTest { postCommand(hueLight, channel, command) - waitForAssert({assertTrue addressWrapper.isSet}, 10000) - waitForAssert({assertTrue bodyWrapper.isSet}, 10000) + waitForAssert({ assertTrue addressWrapper.isSet }, 10000) + waitForAssert({ assertTrue bodyWrapper.isSet }, 10000) assertThat addressWrapper.wrappedObject, is("http://1.2.3.4/api/testUserName/lights/1/state") assertJson(expectedReply, bodyWrapper.wrappedObject) @@ -575,7 +560,7 @@ class HueLightHandlerOSGiTest extends AbstractHueOSGiTest { MockedHttpClient mockedHttpClient) { // mock HttpClient - def hueBridgeField = hueBridgeHandler.getClass().getDeclaredField("bridge") + def hueBridgeField = hueBridgeHandler.getClass().getDeclaredField("hueBridge") hueBridgeField.accessible = true def hueBridgeValue = null diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/HueLightDiscoveryServiceOSGiTest.java b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/HueLightDiscoveryServiceOSGiTest.java index ba4ebdd7c84..f59c8955ded 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/HueLightDiscoveryServiceOSGiTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/HueLightDiscoveryServiceOSGiTest.java @@ -194,7 +194,7 @@ private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttp waitForAssert(() -> { try { // mock HttpClient - final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("bridge"); + final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("hueBridge"); hueBridgeField.setAccessible(true); final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler); assertThat(hueBridgeValue, is(notNullValue())); diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueBridgeNupnpDiscoveryOSGITest.java b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueBridgeNupnpDiscoveryOSGITest.java index 257704b8c0f..9fd92564174 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueBridgeNupnpDiscoveryOSGITest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueBridgeNupnpDiscoveryOSGITest.java @@ -7,6 +7,7 @@ */ package org.eclipse.smarthome.binding.hue.internal.discovery; +import static org.eclipse.smarthome.config.discovery.inbox.InboxPredicates.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -15,13 +16,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.eclipse.smarthome.binding.hue.HueBindingConstants; import org.eclipse.smarthome.config.discovery.DiscoveryListener; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.config.discovery.inbox.Inbox; -import org.eclipse.smarthome.config.discovery.inbox.InboxFilterCriteria; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.test.java.JavaOSGiTest; @@ -48,7 +49,6 @@ public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest { final String sn2 = "001788141b41"; final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1); final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2); - final InboxFilterCriteria inboxFilter = new InboxFilterCriteria(BRIDGE_THING_TYPE_UID, null); final String validBridgeDiscoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1 + "},{\"id\":\"001788fffe141b41\",\"internalipaddress\":" + ip2 + "}]"; String discoveryResult; @@ -155,7 +155,8 @@ public void bridgeThingTypeIsSupported() { @Test public void validBridgesAreDiscovered() { - List oldResults = inbox.get(inboxFilter); + List oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)) + .collect(Collectors.toList()); for (final DiscoveryResult oldResult : oldResults) { inbox.remove(oldResult.getThingUID()); } @@ -190,7 +191,8 @@ public Collection removeOlderResults(DiscoveryService source, long tim assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue())); checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), ip2, sn2); - final List inboxResults = inbox.get(inboxFilter); + final List inboxResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)) + .collect(Collectors.toList()); assertTrue(inboxResults.size() >= 2); assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1)) @@ -202,7 +204,8 @@ public Collection removeOlderResults(DiscoveryService source, long tim @Test public void invalidBridgesAreNotDiscovered() { - List oldResults = inbox.get(inboxFilter); + List oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)) + .collect(Collectors.toList()); for (final DiscoveryResult oldResult : oldResults) { inbox.remove(oldResult.getThingUID()); } @@ -288,7 +291,9 @@ public Collection removeOlderResults(DiscoveryService source, long tim }); waitForAssert(() -> { - assertThat(inbox.get(inboxFilter).size(), is(0)); + assertThat( + inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(), + is(0)); }); } } \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/test/HueLightState.java b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/test/HueLightState.java index 9fe814d36c4..f19543488c7 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/test/HueLightState.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue.test/src/test/java/org/eclipse/smarthome/binding/hue/test/HueLightState.java @@ -92,6 +92,7 @@ public String toString() { " \"name\": \"Hue Light 1\"," + // " \"modelid\": \"" + model + "\"," + // " \"swversion\": \"65003148\"," + // + " \"uniqueid\": \"00:17:88:01:00:e1:88:29-0b\"," + // " \"pointsymbol\": {" + // " \"1\": \"none\"," + // " \"2\": \"none\"," + // diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue.properties b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue.properties index 38d1ed01ec1..91cb5faea2f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue.properties @@ -1 +1,13 @@ -config-status.error.missing-ip-address-configuration=No IP address for the hue bridge has been provided. \ No newline at end of file +# Config status messages +config-status.error.missing-ip-address-configuration=No IP address for the hue bridge has been provided. + +# Thing status descriptions +offline.conf-error-no-ip-address = Cannot connect to hue bridge. IP address not available in configuration. +offline.conf-error-no-username = Cannot connect to hue bridge. User name for authentication not available in configuration. +offline.conf-error-invalid-username = Authentication failed. Remove user name from configuration to generate a new one. +offline.conf-error-press-pairing-button = Not authenticated. Press pairing button on the hue bridge or set a valid user name in configuration. +offline.conf-error-creation-username = Failed to create new user on hue bridge. +offline.bridge-connection-lost = Hue bridge connection lost. +offline.conf-error-no-light-id = Light ID not available in configuration. +offline.light-not-reachable = Hue bridge reports light as not reachable. +offline.light-removed = Hue bridge reports light as removed. diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_de.properties b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_de.properties index 6a01ec388f1..3dc662889db 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_de.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_de.properties @@ -1 +1,74 @@ -config-status.error.missing-ip-address-configuration=Es wurde keine IP Adresse f\u00FCr die Hue Bridge angegeben. \ No newline at end of file +# binding +binding.hue.name = Hue Binding +binding.hue.description = Dieses Binding integriert das Philips Hue System. Durch diese können die Hue Lampen und Leuchten gesteuert werden. + +# thing types +thing-type.hue.bridge.label = Hue Bridge +thing-type.hue.bridge.description = Philips Hue Bridge. +thing-type.hue.0000.label = Lampe (weiß) +thing-type.hue.0000.description = Lampe zum Ein- und Ausschalten. +thing-type.hue.0010.label = Schaltbare Steckdose +thing-type.hue.0010.description = Steckdose zum Ein- und Ausschalten von herkömmlichen Lampen oder anderen elektronischen Geräten. +thing-type.hue.0100.label = Dimmbare Lampe (weiß) +thing-type.hue.0100.description = Dimmbare Lampe mit fester Farbtemperatur. +thing-type.hue.0110.label = Dimmbare Steckdose +thing-type.hue.0110.description = Steckdose zum Dimmen von herkömmlichen Lampen. +thing-type.hue.0200.label = Farbige Lampe +thing-type.hue.0200.description = Dimmbare Lampe mit einstellbarer Farbe. +thing-type.hue.0210.label = Farbige Lampe +thing-type.hue.0210.description = Dimmbare Lampe mit einstellbarer Farbe und Farbtemperatur. +thing-type.hue.0220.label = Farbtemperatur Lampe (weiß) +thing-type.hue.0220.description = Dimmbare Lampe mit einstellbarer Farbtemperatur. + +# thing type configuration +thing-type.config.hue.bridge.ipAddress.label = IP Adresse +thing-type.config.hue.bridge.ipAddress.description = Lokale IP Adresse oder Hostname der Hue Bridge. +thing-type.config.hue.bridge.userName.label = Benutzer +thing-type.config.hue.bridge.userName.description = Benutzer zur Authentifizierung an der Hue Bridge. +thing-type.config.hue.bridge.pollingInterval.label = Abfrageintervall +thing-type.config.hue.bridge.pollingInterval.description = Intervall zur Abfrage der Hue Bridge (in Sekunden). +thing-type.config.hue.0000.lightId.label = ID der Lampe +thing-type.config.hue.0000.lightId.description = ID zur Identifikation der Lampe. +thing-type.config.hue.0010.lightId.label = ID der Steckdose +thing-type.config.hue.0010.lightId.description = ID zur Identifikation der Steckdose. +thing-type.config.hue.0100.lightId.label = ID der Lampe +thing-type.config.hue.0100.lightId.description = ID zur Identifikation der Lampe. +thing-type.config.hue.0110.lightId.label = ID der Steckdose +thing-type.config.hue.0110.lightId.description = ID zur Identifikation der Steckdose. +thing-type.config.hue.0200.lightId.label = ID der Lampe +thing-type.config.hue.0200.lightId.description = ID zur Identifikation der Lampe. +thing-type.config.hue.0210.lightId.label = ID der Lampe +thing-type.config.hue.0210.lightId.description = ID zur Identifikation der Lampe. +thing-type.config.hue.0220.lightId.label = ID der Lampe +thing-type.config.hue.0220.lightId.description = ID zur Identifikation der Lampe. + +# channel types +channel-type.hue.color.label = Farbe +channel-type.hue.color.description = Ermöglicht die Steuerung der Farbe. Ermöglicht ebenfalls die Lampe zu dimmen oder ein- und auszuschalten. +channel-type.hue.brightness.label = Helligkeit +channel-type.hue.brightness.description = Ermöglicht die Steuerung der Helligkeit. Ermöglicht ebenfalls die Lampe ein- und auszuschalten. +channel-type.hue.switch.label = Schalter +channel-type.hue.switch.description = Ermöglicht das Ein- und Ausschalten. +channel-type.hue.color_temperature.label = Farbtemperatur +channel-type.hue.color_temperature.description = Ermöglicht die Steuerung der Farbtemperatur. Von Tageslichtweiß (0) bis Warmweiß (100). +channel-type.hue.alert.label = Alarm +channel-type.hue.alert.description = Ermöglicht ein temporäres Blinken. +channel-type.hue.alert.state.option.NONE = Kein Blinken +channel-type.hue.alert.state.option.SELECT = Einmaliges Blinken +channel-type.hue.alert.state.option.LSELECT = Mehrfaches Blinken +channel-type.hue.effect.label = Farbeffekt +channel-type.hue.effect.description = Ermöglicht einen automatischen Farbwechsels. + +# Config status messages +config-status.error.missing-ip-address-configuration = Es wurde keine IP Adresse für die Hue Bridge angegeben. + +# Thing status descriptions +offline.conf-error-no-ip-address = Verbindung zur Hue Bridge kann nicht aufgebaut werden. Es wurde keine IP Adresse angegeben. +offline.conf-error-no-username = Verbindung zur Hue Bridge kann nicht aufgebaut werden. Es wurde kein Benutzer angegeben. +offline.conf-error-invalid-username = Fehler bei der Authentifizierung. Entfernen Sie den Benutzer zur automatischen Generierung eines neuen Benutzers über die Hue Bridge. +offline.conf-error-press-pairing-button = Bitte drücken Sie den Knopf an der Hue Bridge zur automatischen Generierung eines neuen Benutzers. +offline.conf-error-creation-username = Fehler bei der automatischen Generierung eines neuen Benutzers. +offline.bridge-connection-lost = Verbindung zur Hue Bridge unterbrochen. +offline.conf-error-no-light-id = Es wurde keine ID der Lampe angegeben. +offline.light-not-reachable = Die Hue Bridge meldet, dass die Lampe nicht erreichbar ist. +offline.light-removed = Die Hue Bridge meldet, dass die Lampe entfernt wurde. diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_fr.properties b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_fr.properties new file mode 100644 index 00000000000..f5b100821ee --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/i18n/hue_fr.properties @@ -0,0 +1,74 @@ +# binding +binding.hue.name = Extension hue +binding.hue.description = L'extension hue intègre le système Philips hue et permet de contrôler des ampoules hue. + +# thing types +thing-type.hue.bridge.label = Pont de connexion hue +thing-type.hue.bridge.description = Le pont de connexion Philips Hue. +thing-type.hue.0000.label = Ampoule sans variation +thing-type.hue.0000.description = Une ampoule sans réglage de l'intensité lumineuse. +thing-type.hue.0010.label = Prise commandée sans variation +thing-type.hue.0010.description = Une prise commandée sans réglage de l'intensité de sortie. +thing-type.hue.0100.label = Ampoule à variation +thing-type.hue.0100.description = Une ampoule avec réglage de l'intensité lumineuse. +thing-type.hue.0110.label = Prise commandée à variation +thing-type.hue.0110.description = Une prise commandée avec réglage de l'intensité de sortie. +thing-type.hue.0200.label = Ampoule couleur +thing-type.hue.0200.description = Une ampoule avec réglages de l'intensité lumineuse et de la couleur. +thing-type.hue.0210.label = Ampoule couleur +thing-type.hue.0210.description = Une ampoule avec réglages de l'intensité lumineuse, de la couleur et de la température de couleur +thing-type.hue.0220.label = Ampoule couleur +thing-type.hue.0220.description = Une ampoule avec réglages de l'intensité lumineuse et de la température de couleur. + +# thing type configuration +thing-type.config.hue.bridge.ipAddress.label = Adresse réseau +thing-type.config.hue.bridge.ipAddress.description = L'adresse réseau du pont de connexion hue. +thing-type.config.hue.bridge.userName.label = Nom d'utilisateur +thing-type.config.hue.bridge.userName.description = Le nom d'un utilisateur enregistré sur le pont de connexion hue, autorisant un accès à l'API. +thing-type.config.hue.bridge.pollingInterval.label = Intervalle d'interrogation +thing-type.config.hue.bridge.pollingInterval.description = Le nombre de secondes entre chaque récupération des valeurs du pont. +thing-type.config.hue.0000.lightId.label = ID ampoule +thing-type.config.hue.0000.lightId.description = L'identifiant d'ampoule identifie l'une des ampoules hue. +thing-type.config.hue.0010.lightId.label = ID prise commandée +thing-type.config.hue.0010.lightId.description = L'identifiant de prise commandée identifie l'une des prises commandées. hue +thing-type.config.hue.0100.lightId.label = ID ampoule +thing-type.config.hue.0100.lightId.description = L'identifiant d'ampoule identifie l'une des ampoules hue. +thing-type.config.hue.0110.lightId.label = ID prise commandée +thing-type.config.hue.0110.lightId.description = L'identifiant de prise commandée identifie l'une des prises commandées. hue. +thing-type.config.hue.0200.lightId.label = ID ampoule +thing-type.config.hue.0200.lightId.description = L'identifiant d'ampoule identifie l'une des ampoules hue. +thing-type.config.hue.0210.lightId.label = ID ampoule +thing-type.config.hue.0210.lightId.description = L'identifiant d'ampoule identifie l'une des ampoules hue. +thing-type.config.hue.0220.lightId.label = ID ampoule +thing-type.config.hue.0220.lightId.description = L'identifiant d'ampoule identifie l'une des ampoules hue. + +# channel types +channel-type.hue.color.label = Couleur +channel-type.hue.color.description = Permet de contrôler la couleur de l'ampoule. Il est également possible d'allumer ou d'éteindre l'ampoule ainsi que de faire varier son intensité lumineuse. +channel-type.hue.brightness.label = Intensité lumineuse +channel-type.hue.brightness.description = Permet de contrôler l'intensité lumineuse de l'ampoule. Il est également possible d'allumer ou d'éteindre l'ampoule. +channel-type.hue.switch.label = Commutateur +channel-type.hue.switch.description = Permet d'allumer ou d'éteindre l'ampoule. +channel-type.hue.color_temperature.label = Température couleur +channel-type.hue.color_temperature.description = Permet de choisir la température de couleur de l'ampoule entre 0 (froid) et 100 (chaud). +channel-type.hue.alert.label = Mode alerte +channel-type.hue.alert.description = Permet un changement temporaire de l'état de l'ampoule. +channel-type.hue.alert.state.option.NONE = Sans alerte +channel-type.hue.alert.state.option.SELECT = Alerte courte +channel-type.hue.alert.state.option.LSELECT = Alerte longue +channel-type.hue.effect.label = Mode boucle de couleur +channel-type.hue.effect.description = Permet de passer l'ampoule dans un mode boucle de couleur. + +# Config status messages +config-status.error.missing-ip-address-configuration=Aucune adresse IP fournie pour le pont de connexion hue. + +# Thing status descriptions +offline.conf-error-no-ip-address = Echec de la connexion au pont hue. Adresse IP non renseignée dans la configuration. +offline.conf-error-no-username = Echec de la connexion au pont hue. Nom d''utilisateur pour l''autentification non renseigné dans la configuration. +offline.conf-error-invalid-username = Echec de l''autentification. Supprimer le nom d''utilisateur de la configuration pour en générer un nouveau. +offline.conf-error-press-pairing-button = Non autentifié. Appuyer sur le bouton d'appairage du pont de connexion hue ou définir un nom valide d''utilisateur dans la configuration. +offline.conf-error-creation-username = Echec de la créatiion du nouvel utilisateur sur le pont de connexion hue. +offline.bridge-connection-lost = Perte de la connexion au pont hue. +offline.conf-error-no-light-id = ID ampoule non renseigné dans la configuration. +offline.light-not-reachable = Le pont de connexion hue signale l''ampoule comme inaccessible. +offline.light-removed = Le pont de connexion hue signale l''ampoule comme supprimée. diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorLight.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorLight.xml index 2e5478dfda0..3bdd0c097ae 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorLight.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorLight.xml @@ -19,6 +19,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml index 1d967a1b106..c61b08dfe33 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml @@ -1,6 +1,5 @@ - @@ -19,7 +18,9 @@ - + + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmableLight.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmableLight.xml index 1ce12e9d12e..5b2961ad0b2 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmableLight.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmableLight.xml @@ -18,6 +18,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmablePlug.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmablePlug.xml index 243ca6a1046..f54e857d321 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmablePlug.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/DimmablePlug.xml @@ -17,6 +17,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ExtendedColorLight.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ExtendedColorLight.xml index f6434e98675..ae9db40e5ba 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ExtendedColorLight.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/ExtendedColorLight.xml @@ -20,6 +20,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffLight.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffLight.xml index 34b1670b0e1..c3bbaf73c77 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffLight.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffLight.xml @@ -18,6 +18,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffPlug.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffPlug.xml index f0e80f6292c..48b3f306a58 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffPlug.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/OnOffPlug.xml @@ -17,6 +17,8 @@ + uniqueId + diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/bridge.xml b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/bridge.xml index 57ef46438ca..799b4bdb068 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/bridge.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/ESH-INF/thing/bridge.xml @@ -12,6 +12,7 @@ Philips + serialNumber @@ -28,12 +29,7 @@ false - - - Serial number of the hue bridge. - false - - + Seconds between fetching values from the Bridge. true diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.hue/META-INF/MANIFEST.MF index 975805eeed2..e337af83626 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/META-INF/MANIFEST.MF @@ -11,7 +11,9 @@ Import-Package: com.google.common.collect, com.google.gson.annotations, com.google.gson.reflect, com.google.gson.stream, + org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.hue, org.eclipse.smarthome.binding.hue.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/HueBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/HueBindingConstants.java index da41cbbbbf3..953d0cda204 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/HueBindingConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/HueBindingConstants.java @@ -53,5 +53,7 @@ public class HueBindingConstants { // Light config properties public static final String LIGHT_ID = "lightId"; + public final static String LIGHT_UNIQUE_ID = "uniqueId"; + public final static String MODEL_ID = "modelId"; } diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueBridgeHandler.java index e6890199346..d92b373c3dd 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueBridgeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueBridgeHandler.java @@ -80,17 +80,21 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler { private boolean lastBridgeConnectionState = false; + private boolean propertiesInitializedSuccessfully = false; + private List lightStatusListeners = new CopyOnWriteArrayList<>(); private ScheduledFuture pollingJob; + private HueBridge hueBridge = null; + private Runnable pollingRunnable = new Runnable() { @Override public void run() { try { try { - FullConfig fullConfig = bridge.getFullConfig(); + FullConfig fullConfig = hueBridge.getFullConfig(); if (!lastBridgeConnectionState) { lastBridgeConnectionState = tryResumeBridgeConnection(); } @@ -118,37 +122,29 @@ public void run() { logger.debug("Hue light {} removed.", fullLightEntry.getKey()); for (LightStatusListener lightStatusListener : lightStatusListeners) { try { - lightStatusListener.onLightRemoved(bridge, fullLightEntry.getValue()); + lightStatusListener.onLightRemoved(hueBridge, fullLightEntry.getValue()); } catch (Exception e) { logger.error("An exception occurred while calling the BridgeHeartbeatListener", e); } } } - - final Config config = fullConfig.getConfig(); - if (config != null) { - Map properties = editProperties(); - properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.getMACAddress()); - properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.getSoftwareVersion()); - updateProperties(properties); - } } } catch (UnauthorizedException | IllegalStateException e) { - if (isReachable(bridge.getIPAddress())) { + if (isReachable(hueBridge.getIPAddress())) { lastBridgeConnectionState = false; - onNotAuthenticated(bridge); + onNotAuthenticated(); } else { if (lastBridgeConnectionState || thing.getStatus() == ThingStatus.INITIALIZING) { lastBridgeConnectionState = false; - onConnectionLost(bridge); + onConnectionLost(); } } } catch (Exception e) { - if (bridge != null) { + if (hueBridge != null) { if (lastBridgeConnectionState) { - logger.debug("Connection to Hue Bridge {} lost.", bridge.getIPAddress()); + logger.debug("Connection to Hue Bridge {} lost.", hueBridge.getIPAddress()); lastBridgeConnectionState = false; - onConnectionLost(bridge); + onConnectionLost(); } } } @@ -164,7 +160,7 @@ private boolean isReachable(String ipAddress) { // That's why we do an HTTP access instead // If there is no connection, this line will fail - bridge.authenticate("invalid"); + hueBridge.authenticate("invalid"); } catch (IOException e) { return false; } catch (ApiException e) { @@ -181,10 +177,8 @@ private boolean isReachable(String ipAddress) { } }; - private HueBridge bridge = null; - - public HueBridgeHandler(Bridge hueBridge) { - super(hueBridge); + public HueBridgeHandler(Bridge bridge) { + super(bridge); } @Override @@ -194,9 +188,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { public void updateLightState(FullLight light, StateUpdate stateUpdate) { - if (bridge != null) { + if (hueBridge != null) { try { - bridge.setLightState(light, stateUpdate); + hueBridge.setLightState(light, stateUpdate); } catch (DeviceOffException e) { updateLightState(light, LightStateConverter.toOnOffLightState(OnOffType.ON)); updateLightState(light, stateUpdate); @@ -217,8 +211,8 @@ public void dispose() { pollingJob.cancel(true); pollingJob = null; } - if (bridge != null) { - bridge = null; + if (hueBridge != null) { + hueBridge = null; } } @@ -227,19 +221,19 @@ public void initialize() { logger.debug("Initializing hue bridge handler."); if (getConfig().get(HOST) != null) { - if (bridge == null) { - bridge = new HueBridge((String) getConfig().get(HOST)); - bridge.setTimeout(5000); + if (hueBridge == null) { + hueBridge = new HueBridge((String) getConfig().get(HOST)); + hueBridge.setTimeout(5000); } onUpdate(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Cannot connect to hue bridge. IP address not set."); + "@text/offline.conf-error-no-ip-address"); } } private synchronized void onUpdate() { - if (bridge != null) { + if (hueBridge != null) { if (pollingJob == null || pollingJob.isCancelled()) { int pollingInterval = DEFAULT_POLLING_INTERVAL; try { @@ -254,28 +248,40 @@ private synchronized void onUpdate() { logger.info("Wrong configuration value for polling interval. Using default value: {}s", pollingInterval); } - pollingJob = scheduler.scheduleAtFixedRate(pollingRunnable, 1, pollingInterval, TimeUnit.SECONDS); + pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 1, pollingInterval, TimeUnit.SECONDS); } } } /** - * This method is called whenever the connection to the given {@link HueBridge} is lost. - * - * @param bridge the hue bridge the connection is lost to + * This method is called whenever the connection to the {@link HueBridge} is lost. */ - public void onConnectionLost(HueBridge bridge) { + public void onConnectionLost() { logger.debug("Bridge connection lost. Updating thing status to OFFLINE."); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.bridge-connection-lost"); } /** - * This method is called whenever the connection to the given {@link HueBridge} is resumed. + * This method is called whenever the connection to the {@link HueBridge} is resumed. * - * @param bridge the hue bridge the connection is resumed to + * @throws ApiException if the physical device does not support this API call + * @throws IOException if the physical device could not be reached */ - public void onConnectionResumed(HueBridge bridge) { + private void onConnectionResumed() throws IOException, ApiException { logger.debug("Bridge connection resumed. Updating thing status to ONLINE."); + + if (!propertiesInitializedSuccessfully) { + FullConfig fullConfig = hueBridge.getFullConfig(); + Config config = fullConfig.getConfig(); + if (config != null) { + Map properties = editProperties(); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, config.getMACAddress().replaceAll(":", "").toLowerCase()); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, config.getSoftwareVersion()); + updateProperties(properties); + propertiesInitializedSuccessfully = true; + } + } + updateStatus(ThingStatus.ONLINE); } @@ -283,23 +289,25 @@ public void onConnectionResumed(HueBridge bridge) { * Check USER_NAME config for null. Call onConnectionResumed() otherwise. * * @return True if USER_NAME was not null. + * @throws ApiException if the physical device does not support this API call + * @throws IOException if the physical device could not be reached */ - private boolean tryResumeBridgeConnection() { - logger.debug("Connection to Hue Bridge {} established.", bridge.getIPAddress()); + private boolean tryResumeBridgeConnection() throws IOException, ApiException { + logger.debug("Connection to Hue Bridge {} established.", hueBridge.getIPAddress()); if (getConfig().get(USER_NAME) == null) { logger.warn("User name for Hue bridge authentication not available in configuration. " + "Setting ThingStatus to offline."); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "User name is not properly configured - please check log files"); + "@text/offline.conf-error-no-username"); return false; } else { - onConnectionResumed(bridge); + onConnectionResumed(); return true; } } /** - * This method is called whenever the connection to the given {@link HueBridge} is available, + * This method is called whenever the connection to the {@link HueBridge} is available, * but requests are not allowed due to a missing or invalid authentication. *

    * If there is a user name available, it attempts to re-authenticate. Otherwise new authentication credentials will @@ -308,13 +316,13 @@ private boolean tryResumeBridgeConnection() { * @param bridge the hue bridge the connection is not authorized * @return returns {@code true} if re-authentication was successful, {@code false} otherwise */ - public boolean onNotAuthenticated(HueBridge bridge) { + public boolean onNotAuthenticated() { String userName = (String) getConfig().get(USER_NAME); if (userName == null) { - createUser(bridge); + createUser(); } else { try { - bridge.authenticate(userName); + hueBridge.authenticate(userName); return true; } catch (Exception e) { handleAuthenticationFailure(e, userName); @@ -323,9 +331,9 @@ public boolean onNotAuthenticated(HueBridge bridge) { return false; } - private void createUser(HueBridge bridge) { + private void createUser() { try { - String newUser = createUserOnPhysicalBridge(bridge); + String newUser = createUserOnPhysicalBridge(); updateBridgeThingConfiguration(newUser); } catch (LinkButtonException ex) { handleLinkButtonNotPressed(ex); @@ -334,10 +342,10 @@ private void createUser(HueBridge bridge) { } } - private String createUserOnPhysicalBridge(HueBridge bridge) throws IOException, ApiException { + private String createUserOnPhysicalBridge() throws IOException, ApiException { logger.info("Creating new user on Hue bridge {} - please press the pairing button on the bridge.", getConfig().get(HOST)); - String userName = bridge.link(DEVICE_TYPE); + String userName = hueBridge.link(DEVICE_TYPE); logger.info("User '{}' has been successfully added to Hue bridge.", userName); return userName; } @@ -359,19 +367,19 @@ private void handleAuthenticationFailure(Exception ex, String userName) { logger.warn("User {} is not authenticated on Hue bridge {}", userName, getConfig().get(HOST)); logger.warn("Please configure a valid user or remove user from configuration to generate a new one."); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Authentication failed - remove user name from configuration to generate a new one."); + "@text/offline.conf-error-invalid-username"); } private void handleLinkButtonNotPressed(LinkButtonException ex) { logger.debug("Failed creating new user on Hue bridge: {}", ex.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Not authenticated - press pairing button on the bridge."); + "@text/offline.conf-error-press-pairing-button"); } private void handleExceptionWhileCreatingUser(Exception ex) { logger.warn("Failed creating new user on Hue bridge", ex); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Failed to create new user on bridge: " + ex.getMessage()); + "@text/offline.conf-error-creation-username"); } public boolean registerLightStatusListener(LightStatusListener lightStatusListener) { @@ -383,7 +391,7 @@ public boolean registerLightStatusListener(LightStatusListener lightStatusListen onUpdate(); // inform the listener initially about all lights and their states for (FullLight light : lastLightStates.values()) { - lightStatusListener.onLightAdded(bridge, light); + lightStatusListener.onLightAdded(hueBridge, light); } } return result; @@ -403,33 +411,33 @@ public FullLight getLightById(String lightId) { public List getFullLights() { List ret = withReAuthentication("search for new lights", () -> { - return bridge.getFullConfig().getLights(); + return hueBridge.getFullConfig().getLights(); }); return ret != null ? ret : Collections.emptyList(); } public void startSearch() { withReAuthentication("start search mode", () -> { - bridge.startSearch(); + hueBridge.startSearch(); return null; }); } public void startSearch(List serialNumbers) { withReAuthentication("start search mode", () -> { - bridge.startSearch(serialNumbers); + hueBridge.startSearch(serialNumbers); return null; }); } private T withReAuthentication(String taskDescription, Callable runnable) { - if (bridge != null) { + if (hueBridge != null) { try { try { return runnable.call(); } catch (UnauthorizedException | IllegalStateException e) { lastBridgeConnectionState = false; - if (onNotAuthenticated(bridge)) { + if (onNotAuthenticated()) { return runnable.call(); } } @@ -451,10 +459,10 @@ private void notifyLightStatusListeners(final FullLight fullLight, final String try { switch (type) { case LIGHT_STATE_ADDED: - lightStatusListener.onLightAdded(bridge, fullLight); + lightStatusListener.onLightAdded(hueBridge, fullLight); break; case LIGHT_STATE_CHANGED: - lightStatusListener.onLightStateChanged(bridge, fullLight); + lightStatusListener.onLightStateChanged(hueBridge, fullLight); break; default: throw new IllegalArgumentException( diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueLightHandler.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueLightHandler.java index b14e50efdc5..d42c7054787 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueLightHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/handler/HueLightHandler.java @@ -47,7 +47,8 @@ * @author Dennis Nobel - Initial contribution of hue binding * @author Oliver Libutzki * @author Kai Kreuzer - stabilized code - * @author Andre Fuechsel - implemented switch off when brightness == 0, changed to support generic thing types + * @author Andre Fuechsel - implemented switch off when brightness == 0, changed to support generic thing types, changed + * the initialization of properties * @author Thomas Höfer - added thing properties * @author Jochen Hiller - fixed status updates for reachable=true/false * @author Markus Mazurczak - added code for command handling of OSRAM PAR16 50 @@ -108,8 +109,8 @@ private void initializeThing(ThingStatus bridgeStatus) { // the bridge if (getHueBridgeHandler() != null) { if (bridgeStatus == ThingStatus.ONLINE) { - updateStatus(ThingStatus.ONLINE); initializeProperties(); + updateStatus(ThingStatus.ONLINE); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } @@ -117,7 +118,8 @@ private void initializeThing(ThingStatus bridgeStatus) { updateStatus(ThingStatus.OFFLINE); } } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error-no-light-id"); } } @@ -132,6 +134,7 @@ private synchronized void initializeProperties() { if (vendor != null) { updateProperty(Thing.PROPERTY_VENDOR, vendor); } + updateProperty(LIGHT_UNIQUE_ID, fullLight.getUniqueID()); isOsramPar16 = OSRAM_PAR16_50_TW_MODEL_ID.equals(modelId); propertiesInitializedSuccessfully = true; } @@ -257,7 +260,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (lightState != null) { hueBridge.updateLightState(light, lightState); } else { - logger.warn("Command send to an unknown channel id: " + channelUID); + logger.warn("Command send to an unknown channel id: {}", channelUID); } } @@ -363,7 +366,7 @@ public void onLightStateChanged(HueBridge bridge, FullLight fullLight) { } else { // we assume OFFLINE without any error (NONE), as this is an // expected state (when bulb powered off) - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge reports light as not reachable"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.light-not-reachable"); } HSBType hsbType = LightStateConverter.toHSBType(fullLight.getState()); @@ -401,13 +404,14 @@ public void channelLinked(ChannelUID channelUID) { @Override public void onLightRemoved(HueBridge bridge, FullLight light) { if (light.getId().equals(lightId)) { - updateStatus(ThingStatus.OFFLINE); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "offline.light-removed"); } } @Override public void onLightAdded(HueBridge bridge, FullLight light) { if (light.getId().equals(lightId)) { + initializeProperties(); updateStatus(ThingStatus.ONLINE); onLightStateChanged(bridge, light); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HttpClient.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HttpClient.java index 965161f9d21..2bd9f743d1d 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HttpClient.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HttpClient.java @@ -13,8 +13,8 @@ import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; -import java.util.NoSuchElementException; -import java.util.Scanner; + +import org.apache.commons.io.IOUtils; /** * @@ -60,21 +60,13 @@ protected Result doNetwork(String address, String requestMethod, String body) th } InputStream in = new BufferedInputStream(conn.getInputStream()); - String output = convertStreamToString(in); + String output = IOUtils.toString(in); return new Result(output, conn.getResponseCode()); } finally { conn.disconnect(); } } - private static String convertStreamToString(InputStream is) { - try { - return new Scanner(is).useDelimiter("\\A").next(); - } catch (NoSuchElementException e) { - return ""; - } - } - public static class Result { private String body; private int responseCode; diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HueThingHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HueThingHandlerFactory.java index 8e85d8cf3ef..452ab082be4 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HueThingHandlerFactory.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/HueThingHandlerFactory.java @@ -7,7 +7,7 @@ */ package org.eclipse.smarthome.binding.hue.internal; -import static org.eclipse.smarthome.binding.hue.HueBindingConstants.*; +import static org.eclipse.smarthome.binding.hue.HueBindingConstants.LIGHT_ID; import java.util.HashMap; import java.util.Hashtable; @@ -48,8 +48,7 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory { public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID, ThingUID bridgeUID) { if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { - ThingUID hueBridgeUID = getBridgeThingUID(thingTypeUID, thingUID, configuration); - return super.createThing(thingTypeUID, configuration, hueBridgeUID, null); + return super.createThing(thingTypeUID, configuration, thingUID, null); } if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { ThingUID hueLightUID = getLightUID(thingTypeUID, thingUID, configuration, bridgeUID); @@ -63,14 +62,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES.contains(thingTypeUID); } - private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration) { - if (thingUID == null) { - String serialNumber = (String) configuration.get(SERIAL_NUMBER); - thingUID = new ThingUID(thingTypeUID, serialNumber); - } - return thingUID; - } - private ThingUID getLightUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration, ThingUID bridgeUID) { String lightId = (String) configuration.get(LIGHT_ID); diff --git a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueLightDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueLightDiscoveryService.java index 97768d72d98..78d2dfa754e 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueLightDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.hue/src/main/java/org/eclipse/smarthome/binding/hue/internal/discovery/HueLightDiscoveryService.java @@ -35,7 +35,8 @@ * to a paired hue bridge. The default search time for hue is 60 seconds. * * @author Kai Kreuzer - Initial contribution - * @author Andre Fuechsel - changed search timeout, changed discovery result creation to support generic thing types + * @author Andre Fuechsel - changed search timeout, changed discovery result creation to support generic thing types; + * added representationProperty to discovery result * @author Thomas Höfer - Added representation * @author Denis Dudnik - switched to internally integrated source of Jue library */ @@ -44,7 +45,6 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService implement private final Logger logger = LoggerFactory.getLogger(HueLightDiscoveryService.class); private final static int SEARCH_TIME = 60; - private final static String MODEL_ID = "modelId"; // @formatter:off private final static Map TYPE_TO_ZIGBEE_ID_MAP = new ImmutableMap.Builder() @@ -114,14 +114,11 @@ private void onLightAddedInternal(FullLight light) { Map properties = new HashMap<>(1); properties.put(LIGHT_ID, light.getId()); properties.put(MODEL_ID, modelId); - - /* - * TODO retrieve the light´s unique id (available since Hue bridge versions > 1.3) and set the mac address - * as discovery result representation. For this purpose the jue library has to be modified. - */ + properties.put(LIGHT_UNIQUE_ID, light.getUniqueID()); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) - .withProperties(properties).withBridge(bridgeUID).withLabel(light.getName()).build(); + .withProperties(properties).withBridge(bridgeUID).withRepresentationProperty(LIGHT_UNIQUE_ID) + .withLabel(light.getName()).build(); thingDiscovered(discoveryResult); } else { diff --git a/extensions/binding/org.eclipse.smarthome.binding.lifx/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.lifx/META-INF/MANIFEST.MF index 27799e74617..b8888ef194c 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.lifx/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.lifx/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ClassPath: . Import-Package: com.google.common.collect, javax.xml.bind, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.lifx, org.eclipse.smarthome.binding.lifx.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.lirc/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.lirc/META-INF/MANIFEST.MF index 650c1dbe71b..0ec3283f6cd 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.lirc/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.lirc/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ClassPath: . Import-Package: com.google.common.collect, org.apache.commons.io, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.lirc, org.eclipse.smarthome.binding.lirc.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/handler/LIRCBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/handler/LIRCBridgeHandler.java index 74bcfff484d..048cd8e5318 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/handler/LIRCBridgeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/handler/LIRCBridgeHandler.java @@ -59,7 +59,7 @@ public void initialize() { logger.debug("Initializing the LIRC Bridge handler"); configuration = getConfigAs(LIRCBridgeConfiguration.class); if (connectorTask == null || connectorTask.isCancelled()) { - connectorTask = scheduler.scheduleAtFixedRate(new Runnable() { + connectorTask = scheduler.scheduleWithFixedDelay(new Runnable() { @Override public void run() { logger.debug("Checking LIRC connection, thing status = {}", thing.getStatus()); diff --git a/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/internal/discovery/LIRCRemoteDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/internal/discovery/LIRCRemoteDiscoveryService.java index 6b848ab208c..b45c02e02ef 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/internal/discovery/LIRCRemoteDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.lirc/src/main/java/org/eclipse/smarthome/binding/lirc/internal/discovery/LIRCRemoteDiscoveryService.java @@ -25,6 +25,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * + * @author Andrew Nagle - Initial contribution + */ public class LIRCRemoteDiscoveryService extends AbstractDiscoveryService implements ExtendedDiscoveryService, LIRCMessageListener { diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/META-INF/MANIFEST.MF index 430b275c743..9dc3587baac 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: groovy.lang, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/NtpOSGiTest.launch b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/NtpOSGiTest.launch index a0e0909335e..21bd8193902 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/NtpOSGiTest.launch +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/NtpOSGiTest.launch @@ -1,46 +1,45 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/about.html b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/build.properties b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/build.properties index a01e0b1be5a..df0687569fb 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/build.properties @@ -1,4 +1,5 @@ source.. = src/test/java/ output.. = target/test-classes/ bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/pom.xml index 361c9e68893..46d7dad4e62 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/pom.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/pom.xml @@ -13,6 +13,7 @@ 0.9.0-SNAPSHOT + @@ -54,6 +55,29 @@ ${tycho-groupid} tycho-surefire-plugin + + + eclipse-plugin + org.eclipse.equinox.ds + 0.0.0 + + + + eclipse-plugin + ch.qos.logback.classic + 0.0.0 + + + eclipse-plugin + ch.qos.logback.core + 0.0.0 + + + eclipse-plugin + ch.qos.logback.slf4j + 0.0.0 + + org.eclipse.equinox.ds diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/server/SimpleNTPServer.java b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/server/SimpleNTPServer.java new file mode 100644 index 00000000000..a81d59de0c1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/server/SimpleNTPServer.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.binding.ntp.server; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; + +import org.apache.commons.net.ntp.NtpV3Impl; +import org.apache.commons.net.ntp.NtpV3Packet; +import org.apache.commons.net.ntp.TimeStamp; +import org.eclipse.smarthome.binding.ntp.test.NtpOSGiTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a simple NTP server which provides timestamps to the {@link NtpOSGiTest} tests. + * Its main purpose is to remove the dependence on a remote ntp server because it is hosted locally. + * + * @author Erdoan Hadzhiyusein - Initial Contribution + * + */ +public class SimpleNTPServer { + + private DatagramSocket socket; + private int port; + private volatile boolean isRunning; + private byte[] array = new byte[48]; + private final DatagramPacket request = new DatagramPacket(array, array.length); + private Logger logger = LoggerFactory.getLogger(SimpleNTPServer.class); + + /** + * The server must use an available port to be able to start. + * According to RFC 793, the port is a 16 bit unsigned int. + * + * @param port + */ + public SimpleNTPServer(int port) { + if (port > 0 && port < 65535) { + this.port = port; + } else { + throw new IllegalArgumentException( + "Please choose an available port! This port cannot be used at the moment" + port); + } + } + + /** + * This method opens a new socket and receives requests calling handleRequest() for each one. + */ + public void startServer() { + isRunning = true; + + new Thread() { + @Override + public void run() { + try { + socket = new DatagramSocket(port); + } catch (SocketException e) { + logger.error("Occured an error {}. Couldn't open a socket on this port:", port, e); + } + while (isRunning) { + try { + socket.receive(request); + handleRequest(request); + } catch (IOException e) { + logger.error("There was an error {} while processing the request!", request, e); + } + } + } + }.start(); + } + + /** + * Stopping the server which causes closing the socket too + */ + public void stopServer() { + isRunning = false; + if (socket != null) { + socket.close(); // force closing of the socket + socket = null; + } + } + + private void handleRequest(DatagramPacket requestPacket) throws IOException { + final long receivedTime = System.currentTimeMillis(); + NtpV3Packet responsePacket = new NtpV3Impl(); + responsePacket.setMode(NtpV3Packet.MODE_SERVER); + responsePacket.setTransmitTime(TimeStamp.getNtpTime(receivedTime)); + DatagramPacket dataPacket = responsePacket.getDatagramPacket(); + dataPacket.setPort(requestPacket.getPort()); + dataPacket.setAddress(requestPacket.getAddress()); + socket.send(dataPacket); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/test/NtpOSGiTest.java b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/test/NtpOSGiTest.java index 7538ef7fa19..6df34b05b13 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/test/NtpOSGiTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp.test/src/test/java/org/eclipse/smarthome/binding/ntp/test/NtpOSGiTest.java @@ -20,6 +20,7 @@ import org.apache.commons.lang.StringUtils; import org.eclipse.smarthome.binding.ntp.NtpBindingConstants; import org.eclipse.smarthome.binding.ntp.handler.NtpHandler; +import org.eclipse.smarthome.binding.ntp.server.SimpleNTPServer; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventSubscriber; @@ -38,7 +39,6 @@ import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingProvider; import org.eclipse.smarthome.core.thing.ThingRegistry; -import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.ThingHandler; @@ -83,9 +83,13 @@ public class NtpOSGiTest extends JavaOSGiTest { private static final String TEST_ITEM_NAME = "testItem"; private static final String TEST_THING_ID = "testThingId"; - // No bundle in ESH is exporting a package from which we can use item types as constants, so we will use String. + // No bundle in ESH is exporting a package from which we can use item types + // as constants, so we will use String. private static final String ACCEPTED_ITEM_TYPE_STRING = "String"; private static final String ACCEPTED_ITEM_TYPE_DATE_TIME = "DateTime"; + private static final String TEST_HOSTNAME = "127.0.0.1"; + private static final int TEST_PORT = 9002; + static SimpleNTPServer timeServer; enum UpdateEventType { HANDLE_COMMAND("handleCommand"), @@ -104,16 +108,21 @@ public String getUpdateEventType() { @BeforeClass public static void setUpClass() { + // Initializing a new local server on this port + timeServer = new SimpleNTPServer(TEST_PORT); + // Starting the local server + timeServer.startServer(); + /* - * Store the initial system time zone and locale value, - * so that we can restore them at the test end. + * Store the initial system time zone and locale value, so that we can + * restore them at the test end. */ systemTimeZone = TimeZone.getDefault(); locale = Locale.getDefault(); /* - * Set new default time zone and locale, - * which will be used during the tests execution. + * Set new default time zone and locale, which will be used during the + * tests execution. */ TimeZone.setDefault(TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID)); Locale.setDefault(Locale.US); @@ -148,6 +157,8 @@ public void tearDown() { @AfterClass public static void tearDownClass() { + // Stopping the local time server + timeServer.stopServer(); // Set the default time zone and locale to their initial value. TimeZone.setDefault(systemTimeZone); Locale.setDefault(locale); @@ -160,15 +171,14 @@ public void testStringChannelTimeZoneUpdate() { Configuration configuration = new Configuration(); configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID); - Configuration channelConfig = new Configuration(); /* - * Set the format of the date, so it is updated in the item registry - * in a format from which we can easily get the time zone. + * Set the format of the date, so it is updated in the item registry in + * a format from which we can easily get the time zone. */ channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, TEST_DATE_TIME_FORMAT); - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null); String timeZoneFromItemRegistry = getStringChannelTimeZoneFromItemRegistry(); @@ -183,21 +193,22 @@ public void testDateTimeChannelTimeZoneUpdate() { Configuration configuration = new Configuration(); configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID); - - initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null); + initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null); String testItemState = getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME).toString(); /* - * There is no way to format the date in the dateTime channel - * in advance(there is no property for formatting in the dateTime channel), - * so we will rely on the format, returned by the toString() method of the DateTimeType. + * There is no way to format the date in the dateTime channel in + * advance(there is no property for formatting in the dateTime channel), + * so we will rely on the format, returned by the toString() method of + * the DateTimeType. */ - // FIXME: Adapt the tests if property for formatting in the dateTime channel is added. + // FIXME: Adapt the tests if property for formatting in the dateTime + // channel is added. assertFormat(testItemState, DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS); /* - * Because of the format from the toString() method, - * the time zone will be the last five symbols of - * the string from the item registry(e.g. "+0300" or "-0700"). + * Because of the format from the toString() method, the time zone will + * be the last five symbols of the string from the item registry(e.g. + * "+0300" or "-0700"). */ String timeZoneFromItemRegistry = testItemState.substring(testItemState.length() - expectedTimeZone.length()); @@ -210,7 +221,6 @@ public void testDateTimeChannelTimeZoneUpdate() { public void testDateTimeChannelCalendarTimeZoneUpdate() { Configuration configuration = new Configuration(); configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID); - initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null); String timeZoneIdFromItemRegistry = ((DateTimeType) getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME)).getCalendar() @@ -226,16 +236,15 @@ public void testStringChannelDefaultTimeZoneUpdate() { final String expectedTimeZoneEET = "EET"; Configuration configuration = new Configuration(); - Configuration channelConfig = new Configuration(); /* - * Set the format of the date, so it is updated in the item registry - * in a format from which we can easily get the time zone. + * Set the format of the date, so it is updated in the item registry in + * a format from which we can easily get the time zone. */ channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, TEST_DATE_TIME_FORMAT); // Initialize with configuration with no time zone property set. - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null, null); String timeZoneFromItemRegistry = getStringChannelTimeZoneFromItemRegistry(); @@ -249,17 +258,18 @@ public void testDateTimeChannelDefaultTimeZoneUpdate() { String expectedTimeZone = getDateTimeChannelTimeZone(new DateTimeType(systemCalendar).toString()); Configuration configuration = new Configuration(); - // Initialize with configuration with no time zone property set. - initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null); + initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null); String testItemState = getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME).toString(); /* - * There is no way to format the date in the dateTime channel - * in advance(there is no property for formatting in the dateTime channel), - * so we will rely on the format, returned by the toString() method of the DateTimeType. + * There is no way to format the date in the dateTime channel in + * advance(there is no property for formatting in the dateTime channel), + * so we will rely on the format, returned by the toString() method of + * the DateTimeType. */ - // FIXME: Adapt the tests if property for formatting in the dateTime channel is added. + // FIXME: Adapt the tests if property for formatting in the dateTime + // channel is added. assertFormat(testItemState, DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS); String timeZoneFromItemRegistry = getDateTimeChannelTimeZone(testItemState); @@ -271,9 +281,8 @@ public void testDateTimeChannelDefaultTimeZoneUpdate() { @Test public void testDateTimeChannelCalendarDefaultTimeZoneUpdate() { Configuration configuration = new Configuration(); - // Initialize with configuration with no time zone property set. - initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null); + initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null); String timeZoneIdFromItemRegistry = ((DateTimeType) getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME)).getCalendar() .getTimeZone().getID(); @@ -287,11 +296,10 @@ public void testStringChannelFormatting() { final String formatPattern = "EEE, d MMM yyyy HH:mm:ss Z"; Configuration configuration = new Configuration(); - Configuration channelConfig = new Configuration(); channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, formatPattern); - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null); String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString(); @@ -301,9 +309,8 @@ public void testStringChannelFormatting() { @Test public void testStringChannelDefaultFormatting() { Configuration configuration = new Configuration(); - // Initialize with configuration with no property for formatting set. - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null, null); String dateFromItemRegistryString = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString(); @@ -313,12 +320,11 @@ public void testStringChannelDefaultFormatting() { @Test public void testEmptyStringPropertyFormatting() { Configuration configuration = new Configuration(); - Configuration channelConfig = new Configuration(); // Empty string channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, ""); - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null); String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString(); @@ -328,11 +334,10 @@ public void testEmptyStringPropertyFormatting() { @Test public void testNullPropertyFormatting() { Configuration configuration = new Configuration(); - Configuration channelConfig = new Configuration(); channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, null); - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null); String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString(); @@ -373,6 +378,18 @@ public void testDateTimeChannelLinking() { ACCEPTED_ITEM_TYPE_DATE_TIME); } + private void initialize(Configuration configuration, String channelID, String acceptedItemType, + Configuration channelConfiguration, String wrongHostname) { + // There are 2 tests which require wrong hostnames. + boolean isWrongHostNameTest = wrongHostname != null; + if (isWrongHostNameTest) { + configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_HOST, wrongHostname); + } else { + configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_HOST, TEST_HOSTNAME); + } + initialize(configuration, channelID, acceptedItemType, channelConfiguration); + } + private void initialize(Configuration configuration, String channelID, String acceptedItemType, Configuration channelConfiguration) { ThingUID ntpUid = new ThingUID(NtpBindingConstants.THING_TYPE_NTP, TEST_THING_ID); @@ -385,6 +402,7 @@ private void initialize(Configuration configuration, String channelID, String ac channel = new Channel(channelUID, acceptedItemType); } + configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_PORT, TEST_PORT); ntpThing = ThingBuilder.create(NtpBindingConstants.THING_TYPE_NTP, ntpUid).withConfiguration(configuration) .withChannel(channel).build(); @@ -394,7 +412,6 @@ private void initialize(Configuration configuration, String channelID, String ac ntpHandler = waitForAssert(() -> { final ThingHandler thingHandler = ntpThing.getHandler(); assertThat(thingHandler, is(instanceOf(NtpHandler.class))); - assertThat(ntpThing.getStatus(), is(equalTo(ThingStatus.ONLINE))); return (NtpHandler) thingHandler; }, DFL_TIMEOUT * 3, DFL_SLEEP_TIME); @@ -406,7 +423,8 @@ private void initialize(Configuration configuration, String channelID, String ac itemRegistry.add(testItem); - // Wait for the item , linked to the NTP thing to be added to the ManagedThingProvider. + // Wait for the item , linked to the NTP thing to be added to the + // ManagedThingProvider. final ManagedItemChannelLinkProvider itemChannelLinkProvider = waitForAssert(() -> { final ManagedItemChannelLinkProvider tmp = getService(ManagedItemChannelLinkProvider.class); assertThat("Could not get ManagedItemChannelLinkProvider", tmp, is(notNullValue())); @@ -440,9 +458,9 @@ private State getItemState(String acceptedItemType) { private String getDateTimeChannelTimeZone(String date) { /* - * Because of the format from the toString() method, - * the time zone will be the last five symbols of - * the string from the item registry(e.g. "+0300" or "-0700"). + * Because of the format from the toString() method, the time zone will + * be the last five symbols of the string from the item registry(e.g. + * "+0300" or "-0700"). */ return date.substring(date.length() - 5); } @@ -450,12 +468,13 @@ private String getDateTimeChannelTimeZone(String date) { private String getStringChannelTimeZoneFromItemRegistry() { String itemState = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString(); /* - * This method is used only in tests for the string channel, - * where we have set the format for the date in advance. - * Because of that format, we know that the time zone will be the - * last word of the string from the item registry. + * This method is used only in tests for the string channel, where we + * have set the format for the date in advance. Because of that format, + * we know that the time zone will be the last word of the string from + * the item registry. */ - // FIXME: This can happen a lot easier with Java 8 date time API, so tests can be adapted, if there is an + // FIXME: This can happen a lot easier with Java 8 date time API, so + // tests can be adapted, if there is an // upgrade to Java 8 String timeZoneFromItemRegistry = StringUtils.substringAfterLast(itemState, " "); return timeZoneFromItemRegistry; @@ -479,14 +498,14 @@ private void assertFormat(String initialDate, String formatPattern) { private void assertCommunicationError(String acceptedItemType) { Configuration configuration = new Configuration(); - configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER, "wrong.hostname"); - + final String WRONG_HOSTNAME = "wrong.hostname"; if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_DATE_TIME)) { - initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null); + initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, + WRONG_HOSTNAME); } else if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_STRING)) { - initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null); + initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null, + WRONG_HOSTNAME); } - waitForAssert(() -> { assertThat("The thing status was not communication error", ntpThing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.COMMUNICATION_ERROR))); @@ -495,16 +514,15 @@ private void assertCommunicationError(String acceptedItemType) { private void assertEventIsReceived(UpdateEventType updateEventType, String channelID, String acceptedItemType) { Configuration configuration = new Configuration(); - - initialize(configuration, channelID, acceptedItemType, null); + initialize(configuration, channelID, acceptedItemType, null, null); eventSubscriberMock = new EventSubscriberMock(); registerService(eventSubscriberMock, EventSubscriber.class.getName()); if (updateEventType.equals(UpdateEventType.HANDLE_COMMAND)) { - ntpHandler.handleCommand(null, null); + ntpHandler.handleCommand(new ChannelUID("ntp:test:chan:1"), null); } else if (updateEventType.equals(UpdateEventType.CHANNEL_LINKED)) { - ntpHandler.channelLinked(null); + ntpHandler.channelLinked(new ChannelUID("ntp:test:chan:1")); } waitForAssert(() -> { assertThat("The $channelID channel was not updated on ${updateEventType.getUpdateEventType()} method", diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp.properties b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp.properties new file mode 100644 index 00000000000..7680efab1c7 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp.properties @@ -0,0 +1,4 @@ +# Thing status descriptions +offline.conf-error-init-handler = An error occurred while initializing the NTP handler. +offline.comm-error-unknown-host = The timeserver hostname {0} is unknown -> returning current sytem time instead. +offline.comm-error-connection = The network connection to the timeserver {0} cannot be established -> returning current sytem time instead. diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_de.properties b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_de.properties new file mode 100644 index 00000000000..1363143698f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_de.properties @@ -0,0 +1,36 @@ +# binding +binding.ntp.name = NTP Binding +binding.ntp.description = Dieses Binding fragt das aktuelle Datum und die aktuelle Zeit bei einem Zeitserver ab. + +# thing types +thing-type.ntp.ntp.label = NTP Zeitserver +thing-type.ntp.ntp.description = NTP Zeitserver zur Abfrage des Datums und der Zeit. + +# thing type configuration +thing-type.config.ntp.ntp.hostname.label = IP Adresse +thing-type.config.ntp.ntp.hostname.description = IP Adresse oder Hostname des Zeitservers. +thing-type.config.ntp.ntp.refreshInterval.label = Abfrageintervall +thing-type.config.ntp.ntp.refreshInterval.description = Intervall zur Abfrage des Zeitservers (in Sekunden). +thing-type.config.ntp.ntp.refreshNtp.label = Abfragefrequenz +thing-type.config.ntp.ntp.refreshNtp.description = Anzahl der Aktualisierungen bevor eine Anfrage an den Zeitserver gestellt wird. +thing-type.config.ntp.ntp.serverPort.label = Port +thing-type.config.ntp.ntp.serverPort.description = Port des Zeitservers. +thing-type.config.ntp.ntp.timeZone.label = Zeitzone +thing-type.config.ntp.ntp.timeZone.description = Zeitzone des Systems. +thing-type.config.ntp.ntp.locale.label = Locale +thing-type.config.ntp.ntp.locale.description = Gebietseinstellung des Systems. + +# channel types +channel-type.ntp.dateTime-channel.label = Datum und Zeit +channel-type.ntp.dateTime-channel.description = Zeigt das Datum und die Zeit des Zeitservers an. +channel-type.ntp.string-channel.label = Datum und Zeit +channel-type.ntp.string-channel.description = Zeigt das formatierte Datum und die formatierte Zeit des Zeitservers an. + +# channel type configuration +channel-type.config.ntp.string-channel.DateTimeFormat.label = Datumsformat +channel-type.config.ntp.string-channel.DateTimeFormat.description = Format für die Anzeige des Datum und der Zeit. + +# Thing status descriptions +offline.conf-error-init-handler = Fehler bei der Initialisierung des NTP handler. +offline.comm-error-unknown-host = Zeitserver {0} ist unbekannt. Systemzeit wird zurückgegeben. +offline.comm-error-connection = Verbindung zum Zeitserver {0} kann nicht aufgebaut werden. Systemzeit wird zurückgegeben. diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_fr.properties b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_fr.properties new file mode 100644 index 00000000000..c94fc998135 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/i18n/ntp_fr.properties @@ -0,0 +1,36 @@ +# binding +binding.ntp.name = Extension NTP +binding.ntp.description = L'extension NTP interroge le serveur de temps configuré et fournit la date et l'heure actuelles. + +# thing types +thing-type.ntp.ntp.label = Serveur NTP +thing-type.ntp.ntp.description = Un serveur de temps fournissant la date et l'heure actuelles. + +# thing type configuration +thing-type.config.ntp.ntp.hostname.label = Nom d'hôte du serveur +thing-type.config.ntp.ntp.hostname.description = Le nom d'hôte du serveur de temps. +thing-type.config.ntp.ntp.refreshInterval.label = Fréquence de rafraîchissement +thing-type.config.ntp.ntp.refreshInterval.description = La fréquence en secondes de mise à jour de l'heure. +thing-type.config.ntp.ntp.refreshNtp.label = Fréquence de rafraîchissement NTP +thing-type.config.ntp.ntp.refreshNtp.description = Le nombre de mises à jour avant d'interroger le serveur de temps. +thing-type.config.ntp.ntp.serverPort.label = Port du serveur +thing-type.config.ntp.ntp.serverPort.description = Le port que le serveur de temps peut utiliser. +thing-type.config.ntp.ntp.timeZone.label = Fuseau horaire +thing-type.config.ntp.ntp.timeZone.description = Le fuseau horaire sélectionné. +thing-type.config.ntp.ntp.locale.label = Paramètres régionaux +thing-type.config.ntp.ntp.locale.description = La langue et le pays sélectionnés. + +# channel types +channel-type.ntp.dateTime-channel.label = Date heure +channel-type.ntp.dateTime-channel.description = Date et heure mises à jour à partir du serveur NTP. +channel-type.ntp.string-channel.label = Date heure +channel-type.ntp.string-channel.description = Date et heure mises à jour à partir du serveur NTP. + +# channel type configuration +channel-type.config.ntp.string-channel.DateTimeFormat.label = Format date et heure +channel-type.config.ntp.string-channel.DateTimeFormat.description = Format utilisé pour la présentation de la date et de l'heure. + +# Thing status descriptions +offline.conf-error-init-handler = Une erreur est survenue durant l''initialisation. +offline.comm-error-unknown-host = Le nom d''hôte {0} du serveur de temps est inconnu -> renvoi de l''heure courante système à la place. +offline.comm-error-connection = La connexion réseau avec le serveur de temps {0} ne peut pas être établie -> renvoi de l''heure courante système à la place. diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/thing/thing-types.xml b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/thing/thing-types.xml index 34abd141736..cdb620c8998 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/thing/thing-types.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/ESH-INF/thing/thing-types.xml @@ -1,5 +1,6 @@ - @@ -17,7 +18,7 @@ The NTP server hostname. 0.pool.ntp.org
    - + Interval that new time updates are posted to the event bus in seconds. @@ -31,6 +32,11 @@ 30 + + + The port that the NTP server could use. + 123 + The configured timezone. diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.ntp/META-INF/MANIFEST.MF index d8e4c2ac636..a2cde436163 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ClassPath: . Import-Package: org.apache.commons.net, org.apache.commons.net.ntp, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.i18n, diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/README.md b/extensions/binding/org.eclipse.smarthome.binding.ntp/README.md index f21478fd632..592ae2173ed 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/README.md +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/README.md @@ -30,6 +30,7 @@ The thing has a few configuration options: ## Channels The ntp binding has two channels: + * `dateTime` which provides the data in a dateTime type * `string` which provides the data in a string type. The string channel can be configured with the formatting of the date & time. This also allows proper representation of timezones other than the java machine default one. See the [java documentation](http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html) for the detailed information on the formatting @@ -43,6 +44,7 @@ Things: ``` ntp:ntp:demo [ hostname="nl.pool.ntp.org", refreshInterval=60, refreshNtp=30 ] ``` + Items: ``` diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/about.html b/extensions/binding/org.eclipse.smarthome.binding.ntp/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/build.properties b/extensions/binding/org.eclipse.smarthome.binding.ntp/build.properties index 000767b3bf4..a8867c638eb 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/build.properties @@ -3,4 +3,5 @@ output.. = target/classes bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - ESH-INF/ + ESH-INF/,\ + about.html diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/NtpBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/NtpBindingConstants.java index 3d35ed5c3cd..b58f9bbd8da 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/NtpBindingConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/NtpBindingConstants.java @@ -31,12 +31,13 @@ public class NtpBindingConstants { public final static String CHANNEL_STRING = "string"; // Custom Properties - public final static String PROPERTY_NTP_SERVER = "hostname"; + public final static String PROPERTY_NTP_SERVER_HOST = "hostname"; public final static String PROPERTY_REFRESH_INTERVAL = "refreshInterval"; public final static String PROPERTY_REFRESH_NTP = "refreshNtp"; public final static String PROPERTY_TIMEZONE = "timeZone"; public final static String PROPERTY_LOCALE = "locale"; public final static String PROPERTY_DATE_TIME_FORMAT = "DateTimeFormat"; + public final static String PROPERTY_NTP_SERVER_PORT = "serverPort"; public final static Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_NTP); diff --git a/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/handler/NtpHandler.java b/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/handler/NtpHandler.java index 784546da317..613e1028c75 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/handler/NtpHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.ntp/src/main/java/org/eclipse/smarthome/binding/ntp/handler/NtpHandler.java @@ -56,7 +56,7 @@ public class NtpHandler extends BaseThingHandler { private Logger logger = LoggerFactory.getLogger(NtpHandler.class); /** timeout for requests to the NTP server */ - private static final int NTP_TIMEOUT = 10000; + private static final int NTP_TIMEOUT = 30000; public static final String DATE_PATTERN_WITH_TZ = "yyyy-MM-dd HH:mm:ss z"; @@ -72,6 +72,8 @@ public class NtpHandler extends BaseThingHandler { /** NTP host */ private String hostname; + /** NTP server port */ + private BigDecimal port; /** refresh interval */ private BigDecimal refreshInterval; /** NTP refresh frequency */ @@ -108,7 +110,8 @@ public void initialize() { logger.debug("Initializing NTP handler for '{}'.", getThing().getUID().toString()); Configuration config = getThing().getConfiguration(); - hostname = (String) config.get(PROPERTY_NTP_SERVER); + hostname = (String) config.get(PROPERTY_NTP_SERVER_HOST); + port = (BigDecimal) config.get(PROPERTY_NTP_SERVER_PORT); refreshInterval = (BigDecimal) config.get(PROPERTY_REFRESH_INTERVAL); refreshNtp = (BigDecimal) config.get(PROPERTY_REFRESH_NTP); refreshNtpCount = 0; @@ -135,8 +138,9 @@ public void initialize() { String dateTimeFormatString = (String) cfg.get(PROPERTY_DATE_TIME_FORMAT); if (!(dateTimeFormatString == null || dateTimeFormatString.isEmpty())) { dateTimeFormat = new SimpleDateFormat(dateTimeFormatString); - logger.debug("Could not format {} with DateFormat '{}', using default format.", - getThing().getUID().toString(), dateTimeFormatString); + } else { + logger.debug("No format set in channel config for {}. Using default format.", stringChannelUID); + dateTimeFormat = new SimpleDateFormat(DATE_PATTERN_WITH_TZ); } } catch (Exception ex) { logger.debug("No channel config or invalid format for {}. Using default format. ({})", stringChannelUID, @@ -153,8 +157,9 @@ public void initialize() { } catch (Exception ex) { String msg = "Error occurred while initializing NTP handler: " + ex.getMessage(); - logger.error(msg, ex); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg); + logger.error("{}", msg, ex); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error-init-handler"); } } @@ -177,7 +182,7 @@ public void run() { } }; - refreshJob = scheduler.scheduleAtFixedRate(runnable, 0, refreshInterval.intValue(), TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(runnable, 0, refreshInterval.intValue(), TimeUnit.SECONDS); } private synchronized void refreshTimeDate() { @@ -207,8 +212,7 @@ private synchronized void refreshTimeDate() { * Queries the given timeserver hostname and returns the time * in milliseconds. * - * @param hostname - * the timeserver to query + * @param hostname - the timeserver to query * @return the time in milliseconds or the current time of the system if an * error occurs. */ @@ -218,7 +222,7 @@ public long getTime(String hostname) { NTPUDPClient timeClient = new NTPUDPClient(); timeClient.setDefaultTimeout(NTP_TIMEOUT); InetAddress inetAddress = InetAddress.getByName(hostname); - TimeInfo timeInfo = timeClient.getTime(inetAddress); + TimeInfo timeInfo = timeClient.getTime(inetAddress, port.intValue()); logger.debug("{} Got time update from: {} : {}", getThing().getUID().toString(), hostname, SDF.format(new Date(timeInfo.getReturnTime()))); @@ -227,13 +231,15 @@ public long getTime(String hostname) { } catch (UnknownHostException uhe) { String msg = getThing().getUID().toString() + " the given hostname '" + hostname + "' of the timeserver is unknown -> returning current sytem time instead."; - logger.warn(msg); - updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR, msg); + logger.debug("{}", msg); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/offline.comm-error-unknown-host [\"" + (hostname == null ? "null" : hostname) + "\"]"); } catch (IOException ioe) { String msg = getThing().getUID().toString() + " couldn't establish network connection [host '" + hostname + "'] -> returning current sytem time instead."; - logger.warn(msg); - updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR, msg); + logger.debug("{}", msg); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/offline.comm-error-connection [\"" + (hostname == null ? "null" : hostname) + "\"]"); } return System.currentTimeMillis(); diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/config/config.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/config/config.xml index d1999f29d0f..8f5742afc94 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/config/config.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/config/config.xml @@ -3,19 +3,18 @@ xmlns:config-description="http://eclipse.org/smarthome/schemas/config-description/v1.0.0" xsi:schemaLocation="http://eclipse.org/smarthome/schemas/config-description/v1.0.0 http://eclipse.org/smarthome/schemas/config-description-1.0.0.xsd" > - + The UDN identifies the Zone Player. true - + - Specifies the amount of time for which the notification sound will be played - 40 - true + Specifies the amount of time in seconds for which the notification sound will be played + 20 - + Specifies the refresh interval in seconds 60 diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos.properties b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos.properties new file mode 100644 index 00000000000..72031ee4e04 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos.properties @@ -0,0 +1,4 @@ +# Thing status descriptions +offline.conf-error-missing-udn = The parameter "Unique Device Name" must be configured. +offline.upnp-device-not-registered = The UPnP device {0} is not yet registered. +offline.not-available-on-network = The Sonos player {0} is not available in local network. diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos_fr.properties b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos_fr.properties new file mode 100644 index 00000000000..d9517b8c072 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/i18n/sonos_fr.properties @@ -0,0 +1,127 @@ +# binding +binding.sonos.name = Extension Sonos +binding.sonos.description = Il s'agit de l'extension pour le système audio multiroom Sonos. +binding.config.sonos.opmlUrl.label = URL service OPML +binding.config.sonos.opmlUrl.description = URL pour le service OPML/tunein.com +binding.config.sonos.callbackUrl.label = URL callback +binding.config.sonos.callbackUrl.description = URL à utiliser pour les notifications sonores, par ex. http://192.168.0.2:8080 + +# thing types +thing-type.sonos.CONNECT.label = CONNECT +thing-type.sonos.CONNECT.description = Représente un lecteur pré-ampli Sonos CONNECT +thing-type.sonos.CONNECTAMP.label = CONNECT:AMP +thing-type.sonos.CONNECTAMP.description = Représente un lecteur ampli Sonos CONNECT:AMP +thing-type.sonos.PLAY1.label = PLAY:1 +thing-type.sonos.PLAY1.description = Représente une enceinte Sonos PLAY:1 +thing-type.sonos.PLAY3.label = PLAY:3 +thing-type.sonos.PLAY3.description = Représente une enceinte Sonos PLAY:3 +thing-type.sonos.PLAY5.label = PLAY:5 +thing-type.sonos.PLAY5.description = Représente une enceinte Sonos PLAY:5 +thing-type.sonos.PLAYBAR.label = PLAYBAR +thing-type.sonos.PLAYBAR.description = Représente une barre de son Sonos +thing-type.sonos.zoneplayer.label = Autre Sonos +thing-type.sonos.zoneplayer.description = Représente un équipement Sonos inconnu de l'extension + +thing-type.config.sonos.zoneplayer.udn.label = Nom unique (UDN) +thing-type.config.sonos.zoneplayer.udn.description = Identifie de manière unique un Sonos +thing-type.config.sonos.zoneplayer.notificationTimeout.label = Délai max de notification +thing-type.config.sonos.zoneplayer.notificationTimeout.description = Spécifie le délai maximum en secondes pendant lequel les notifications sonores sont lues +thing-type.config.sonos.zoneplayer.refresh.label = Fréquence de rafraîchissement +thing-type.config.sonos.zoneplayer.refresh.description = Spécifie la fréquence de rafraîchissement en secondes + +# channel types +channel-type.sonos.add.label = Ajoute au groupe +channel-type.sonos.add.description = Ajoute le Sonos désigné au groupe de ce Sonos +channel-type.sonos.alarm.label = Active l'alarme +channel-type.sonos.alarm.description = Active la première alarme (qu'elle soit déjà active ou pas). Des alarmes doivent avoir d'abord été définies avec l'application Sonos +channel-type.sonos.alarmproperties.label = Caractéristiques alarme +channel-type.sonos.alarmproperties.description = Les caractéristiques de l'alarme en cours +channel-type.sonos.alarmrunning.label = Alarme en cours +channel-type.sonos.alarmrunning.description = Indique si une alarme est en cours +channel-type.sonos.clearqueue.label = Vide la file d'attente +channel-type.sonos.clearqueue.description = Supprime toutes les chansons de la file d'attente +channel-type.sonos.control.label = Contrôle du lecteur +channel-type.sonos.control.description = Contrôle ce Sonos: lecture, pause, stop, chanson suivante, chanson précédente, avance, retour arrière +channel-type.sonos.coordinator.label = Coordinateur du groupe +channel-type.sonos.coordinator.description = L'identifiant du Sonos qui coordonne le groupe actuel +channel-type.sonos.currentalbum.label = Album +channel-type.sonos.currentalbum.description = Le nom de l'album en cours de lecture +channel-type.sonos.currentalbumart.label = Pochette album +channel-type.sonos.currentalbumart.description = La pochette de l'album en cours de lecture +channel-type.sonos.currentalbumarturl.label = URL pochette album +channel-type.sonos.currentalbumarturl.description = L'URL de la pochette de l'album en cours de lecture +channel-type.sonos.currentartist.label = Artiste +channel-type.sonos.currentartist.description = Le nom de l'artiste en cours de lecture +channel-type.sonos.currenttitle.label = Titre +channel-type.sonos.currenttitle.description = Le titre de la chanson en cours de lecture +channel-type.sonos.currenttrack.label = Piste +channel-type.sonos.currenttrack.description = La piste ou la station de radio en cours de lecture +channel-type.sonos.currenttrackuri.label = URI piste +channel-type.sonos.currenttrackuri.description = l'URI de la piste en cours +channel-type.sonos.currenttransporturi.label = URI transport AV +channel-type.sonos.currenttransporturi.description = L'URI du transport AV en cours +channel-type.sonos.favorite.label = Lit un favori +channel-type.sonos.favorite.description = Lit le favori Sonos désigné. Le favori Sonos doit d'abord avoir été défini avec l'application Sonos +channel-type.sonos.led.label = Led +channel-type.sonos.led.description = Indique ou modifie l'état de la Led blanche en façade de ce Sonos +channel-type.sonos.localcoordinator.label = Coordonne le groupe +channel-type.sonos.localcoordinator.description = Indique si ce Sonos est le coordinateur du groupe +channel-type.sonos.mute.label = Son coupé +channel-type.sonos.mute.description = Indique pu modifie l'état relatif à la coupure du son +channel-type.sonos.notificationsound.label = Lit une notification sonore +channel-type.sonos.notificationsound.description = Lit la notification sonore désignée par l'URI fournie +channel-type.sonos.notificationvolume.label = Volume notification sonore +channel-type.sonos.notificationvolume.description = Définit le volume utilisé pour une notification sonore +channel-type.sonos.playlist.label = Lit une liste de lecture +channel-type.sonos.playlist.description = Lit la liste de lecture désignée. La liste de lecture doit d'abord avoir été définie avec l'application Sonos +channel-type.sonos.playqueue.label = Lit la file d'attente +channel-type.sonos.playqueue.description = Lit les chansons dans la file d'attente +channel-type.sonos.playtrack.label = Lit une piste +channel-type.sonos.playtrack.description = Lit le numéro de piste désigné de la file d'attente +channel-type.sonos.playuri.label = Lit une URI +channel-type.sonos.playuri.description = Lit l'URI désignée +channel-type.sonos.publicaddress.label = Adresse publique +channel-type.sonos.publicaddress.description = Crée un groupe avec tous les Sonos et lit l'entrée source locale du Sonos qui reçoit cette commande +channel-type.sonos.radio.label = Lit une station radio +channel-type.sonos.radio.description = Lit la station radio favorite désignée. Les station favorites doivent d'abord avoir été définies avec l'application Sonos +channel-type.sonos.remove.label = Retire du groupe +channel-type.sonos.remove.description = Retire le Sonos désigné de son groupe +channel-type.sonos.repeat.label = Mode de répétition +channel-type.sonos.repeat.description = Indique ou modifie le mode de répétition (sans, piste, file) +channel-type.sonos.restore.label = Restaure l'état +channel-type.sonos.restore.description = Restaure l'état sauvegardé de ce Sonos +channel-type.sonos.restoreall.label = Restaure l'état de tous les Sonos +channel-type.sonos.restoreall.description = Restaure l'état sauvegardé de chaque Sonos +channel-type.sonos.save.label = Sauve l'état +channel-type.sonos.save.description = Sauve l'état actuel de ce Sonos +channel-type.sonos.saveall.label = Sauve l'état de tous les Sonos +channel-type.sonos.saveall.description = Sauve l'état actuel de chaque Sonos +channel-type.sonos.shuffle.label = Mode de lecture aléatoire +channel-type.sonos.shuffle.description = Indique pu modifie le mode aléatoire de lecture des pistes de la file d'attente +channel-type.sonos.sleeptimer.label = Horloge de veille +channel-type.sonos.sleeptimer.description = Indique ou définit le temps restant en secondes avant la mise en veille de ce Sonos +channel-type.sonos.snooze.label = Gèle l'alarme +channel-type.sonos.snooze.description = Gèle l'alarme en cours du nombre de minutes précisé +channel-type.sonos.standalone.label = Lecteur autonome +channel-type.sonos.standalone.description = Sort ce Sonos de tout groupe et le rend autonome +channel-type.sonos.state.label = Etat du lecteur +channel-type.sonos.state.description = Etat de lecture de ce Sonos, par exemple PLAYING, STOPPED,... +channel-type.sonos.stop.label = Stop lecture +channel-type.sonos.stop.description = Stoppe la lecture par ce Sonos +channel-type.sonos.volume.label = Volume +channel-type.sonos.volume.description = Indique ou modifie le volume de ce Sonos +channel-type.sonos.zonegroup.label = Zone Group +channel-type.sonos.zonegroup.description = Chaîne au format XML contenant la configuration de tous les groupes +channel-type.sonos.zonegroupid.label = Zone Group ID +channel-type.sonos.zonegroupid.description = Identifiant du groupe auquel appartient ce Sonos +channel-type.sonos.zonename.label = Pièce +channel-type.sonos.zonename.description = Pièce associée à ce Sonos +channel-type.sonos.linein.label = Entrée source connectée +channel-type.sonos.linein.description = Indique si l'entrée source locale de ce Sonos est connectée +channel-type.sonos.playlinein.label = Lit une entrée source +channel-type.sonos.playlinein.description = Lit l'entrée source du Sonos désigné + +# Thing status descriptions +offline.conf-error-missing-udn = Le paramètre "Nom unique (UDN)" doit être configuré. +offline.upnp-device-not-registered = Le périphérique UPnP {0} n''est pas encore découvert. +offline.not-available-on-network = Le lecteur Sonos {0} n''est pas disponible sur le réseau local. diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECT.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECT.xml index f3d96130a08..3697ad3d317 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECT.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECT.xml @@ -59,9 +59,11 @@ SONOS - + CONNECT - + udn + +
    diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECTAMP.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECTAMP.xml index 2ba6bf8b12c..003926bfbac 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECTAMP.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/CONNECTAMP.xml @@ -63,6 +63,8 @@ CONNECT:AMP - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY1.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY1.xml index a356d912dfa..fa10475c899 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY1.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY1.xml @@ -60,6 +60,8 @@ PLAY:1 - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY3.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY3.xml index 1a7f46c4859..bdfd255ed60 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY3.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY3.xml @@ -60,6 +60,8 @@ PLAY:3 - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY5.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY5.xml index 5cc735abc7a..e308060af7b 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY5.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAY5.xml @@ -63,6 +63,8 @@ PLAY:5 - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAYBAR.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAYBAR.xml index 54504257e8d..a02e8801b3c 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAYBAR.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/PLAYBAR.xml @@ -62,6 +62,8 @@ PLAYBAR - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/ZonePlayer.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/ZonePlayer.xml index a2405c09ce2..a326b9c3f49 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/ZonePlayer.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/ZonePlayer.xml @@ -60,6 +60,8 @@ ZonePlayer - + udn + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/channels.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/channels.xml index 38b77ee6fb4..2e973f39d7a 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/channels.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/thing/channels.xml @@ -7,25 +7,33 @@ String - Add a Zone Player to the group of the given Zone Player + Add the given Zone Player to the group of this Zone Player Switch - Set the first occurring alarm either ON or OFF. Alarms have first have to be defined through the Sonos Controller app + Set the first occurring alarm either ON or OFF. Alarms first have to be defined through the Sonos Controller app String Properties of the alarm currently running + Switch Set to ON if the alarm was triggered + + + + + Switch + + Suppress all songs from the current queue @@ -35,40 +43,67 @@ Player + + String + + UDN of the coordinator for the current group + + + String Name of the album currently playing + Image Cover art of the album currently playing + String Cover art URL of the album currently playing + String Name of the artist currently playing + String Title of the song currently playing + String Name of the current track or radio station currently playing + + + + + String + + URI of the current track + + + + + String + + URI of the current AV transport + @@ -87,6 +122,7 @@ Switch Indicator set to ON if the this Zone Player is the Zone Group Coordinator + @@ -114,12 +150,6 @@ Play the given playlist. The playlist has to predefined in the Sonos Controller app - - Switch - - Suppress all songs from the current queue - - Switch @@ -156,6 +186,12 @@ Remove the given Zone Player to the group of this Zone Player + + String + + Repeat track or queue playback + + Switch @@ -180,10 +216,24 @@ Save the state of all the Zone Players + + Switch + + Shuffle queue playback + + + + Number + + Set/show the duration of the SleepTimer in seconds + + + Number Snooze the running alarm, if any, with the given number of minutes + @@ -197,6 +247,7 @@ String The State channel contains state of the Zone Player, e.g. PLAYING, STOPPED,... + @@ -216,24 +267,21 @@ String XML formatted string with the current zonegroup configuration + String Id of the Zone Group the Zone Player belongs to + String - Name of the Zone Group the Zone Player belongs to - - - - String - - UDN of the coordinator for the current group + Name of the Zone associated to the Zone Player + @@ -241,6 +289,7 @@ Switch Indicator set to ON when the line-in of the Zone Player is connected + @@ -249,36 +298,4 @@ Play the Line-in of the the Zone Player corresponding to the given UIN - - Number - - Set/show the duration of the SleepTimer in seconds - - - - - - Switch - - Shuffle queue playback - - - - String - - Repeat track or queue playback - - - - String - - URI of the current AV transport - - - - String - - URI of the current track - - diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF index 62e5e869ee7..4c68c676bad 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF @@ -9,11 +9,13 @@ Bundle-ClassPath: . Import-Package: com.google.common.collect, org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.sonos, org.eclipse.smarthome.binding.sonos.handler, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.audio, + org.eclipse.smarthome.core.audio.utils, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.net, diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml deleted file mode 100644 index 57a1f8dae10..00000000000 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/ZonePlayerDiscovery.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/ZonePlayerDiscovery.xml deleted file mode 100644 index a139a62d47a..00000000000 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/ZonePlayerDiscovery.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/README.md b/extensions/binding/org.eclipse.smarthome.binding.sonos/README.md index e90b38a3aba..629629d0b66 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/README.md +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/README.md @@ -27,6 +27,7 @@ The binding has the following configuration options, which can be set for "bindi The Sonos Thing requires the UPnP UDN (Unique Device Name) as a configuration value in order for the binding to know how to access it. All the Sonos UDN have the "RINCON_000E58D8403A0XXXX" format. Additionally, a refresh interval, used to poll the Sonos device, can be specified (in seconds) In the thing file, this looks e.g. like + ``` Thing sonos:PLAY1:1 [ udn="RINCON_000E58D8403A0XXXX", refresh=60] ``` diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/build.properties b/extensions/binding/org.eclipse.smarthome.binding.sonos/build.properties index 66e21b90751..e8c3f14cfdf 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/build.properties @@ -3,4 +3,6 @@ output.. = target/classes bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - ESH-INF/ \ No newline at end of file + ESH-INF/,\ + about.html + diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/SonosBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/SonosBindingConstants.java index cde1b5a1daf..5193880eb97 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/SonosBindingConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/SonosBindingConstants.java @@ -52,13 +52,17 @@ public class SonosBindingConstants { public final static String ALARM = "alarm"; public final static String ALARMPROPERTIES = "alarmproperties"; public final static String ALARMRUNNING = "alarmrunning"; + public final static String CLEARQUEUE = "clearqueue"; public final static String CONTROL = "control"; + public final static String COORDINATOR = "coordinator"; public final static String CURRENTALBUM = "currentalbum"; public final static String CURRENTALBUMART = "currentalbumart"; public final static String CURRENTALBUMARTURL = "currentalbumarturl"; public final static String CURRENTARTIST = "currentartist"; public final static String CURRENTTITLE = "currenttitle"; public final static String CURRENTTRACK = "currenttrack"; + public final static String CURRENTTRACKURI = "currenttrackuri"; + public final static String CURRENTTRANSPORTURI = "currenttransporturi"; public final static String FAVORITE = "favorite"; public final static String LED = "led"; public final static String LINEIN = "linein"; @@ -68,17 +72,19 @@ public class SonosBindingConstants { public final static String NOTIFICATIONVOLUME = "notificationvolume"; public final static String PLAYLINEIN = "playlinein"; public final static String PLAYLIST = "playlist"; - public final static String CLEARQUEUE = "clearqueue"; public final static String PLAYQUEUE = "playqueue"; public final static String PLAYTRACK = "playtrack"; public final static String PLAYURI = "playuri"; public final static String PUBLICADDRESS = "publicaddress"; public final static String RADIO = "radio"; public final static String REMOVE = "remove"; + public final static String REPEAT = "repeat"; public final static String RESTORE = "restore"; public final static String RESTOREALL = "restoreall"; public final static String SAVE = "save"; public final static String SAVEALL = "saveall"; + public final static String SHUFFLE = "shuffle"; + public final static String SLEEPTIMER = "sleeptimer"; public final static String SNOOZE = "snooze"; public final static String STANDALONE = "standalone"; public final static String STATE = "state"; @@ -87,15 +93,11 @@ public class SonosBindingConstants { public final static String ZONEGROUP = "zonegroup"; public final static String ZONEGROUPID = "zonegroupid"; public final static String ZONENAME = "zonename"; - public final static String COORDINATOR = "coordinator"; public final static String MODELID = "modelId"; - public final static String SLEEPTIMER = "sleeptimer"; - public final static String SHUFFLE = "shuffle"; - public final static String REPEAT = "repeat"; - public final static String CURRENTTRANSPORTURI = "currenttransporturi"; - public final static String CURRENTTRACKURI = "currenttrackuri"; // List of properties public static final String IDENTIFICATION = "identification"; + public static final String MAC_ADDRESS = "macAddress"; + public static final String IP_ADDRESS = "ipAddress"; } diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/config/ZonePlayerConfiguration.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/config/ZonePlayerConfiguration.java index f6c447eca89..096b713ad5c 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/config/ZonePlayerConfiguration.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/config/ZonePlayerConfiguration.java @@ -7,6 +7,10 @@ */ package org.eclipse.smarthome.binding.sonos.config; +/** + * + * @author Karel Goderis - Initial contribution + */ public class ZonePlayerConfiguration { public static final String UDN = "udn"; diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/discovery/ZonePlayerDiscoveryParticipant.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/discovery/ZonePlayerDiscoveryParticipant.java index 4d21fc2711a..4d18ab292c4 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/discovery/ZonePlayerDiscoveryParticipant.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/discovery/ZonePlayerDiscoveryParticipant.java @@ -20,6 +20,7 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.jupnp.model.meta.RemoteDevice; +import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,7 @@ * * @author Karel Goderis - Initial contribution */ +@Component(immediate = true) public class ZonePlayerDiscoveryParticipant implements UpnpDiscoveryParticipant { private Logger logger = LoggerFactory.getLogger(ZonePlayerDiscoveryParticipant.class); @@ -51,11 +53,12 @@ public DiscoveryResult createResult(RemoteDevice device) { } catch (Exception e) { // ignore and use default label } + label += " (" + roomName + ")"; properties.put(ZonePlayerConfiguration.UDN, device.getIdentity().getUdn().getIdentifierString()); properties.put(SonosBindingConstants.IDENTIFICATION, roomName); DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label) - .withRepresentationProperty(SonosBindingConstants.IDENTIFICATION).build(); + .withRepresentationProperty(ZonePlayerConfiguration.UDN).build(); logger.debug("Created a DiscoveryResult for device '{}' with UDN '{}'", device.getDetails().getFriendlyName(), device.getIdentity().getUdn().getIdentifierString()); diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java index b07651235b3..93025367c34 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java @@ -37,7 +37,6 @@ import org.eclipse.smarthome.binding.sonos.internal.SonosXMLParser; import org.eclipse.smarthome.binding.sonos.internal.SonosZoneGroup; import org.eclipse.smarthome.binding.sonos.internal.SonosZonePlayerState; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryServiceRegistry; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; @@ -86,6 +85,7 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici private final static String QUEUE_URI = "x-rincon-queue:"; private final static String GROUP_URI = "x-rincon:"; private final static String STREAM_URI = "x-sonosapi-stream:"; + private final static String RADIO_URI = "x-sonosapi-radio:"; private final static String FILE_URI = "x-file-cifs:"; private final static String SPDIF = ":spdif"; @@ -101,12 +101,12 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici private static final int SOCKET_TIMEOUT = 5000; /** - * Default notification timeout + * Default notification timeout (in seconds) */ - private static final Integer DEFAULT_NOTIFICATION_TIMEOUT = 40000; - + private static final Integer DEFAULT_NOTIFICATION_TIMEOUT = 20; + /** - * configurable notification timeout + * configurable notification timeout (in seconds) */ private Integer notificationTimeout = null; @@ -147,7 +147,8 @@ public void run() { // If not, set the thing state to OFFLINE and wait for the next poll if (!isUpnpDeviceRegistered()) { logger.debug("UPnP device {} not yet registered", getUDN()); - updateStatus(ThingStatus.OFFLINE); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/offline.upnp-device-not-registered [\"" + getUDN() + "\"]"); synchronized (upnpLock) { subscriptionState = new HashMap(); } @@ -212,19 +213,15 @@ public void initialize() { if (getUDN() != null) { onUpdate(); - - if (getConfigAs(ZonePlayerConfiguration.class).notificationTimeout == null) { - Configuration c = editConfiguration(); - c.put(ZonePlayerConfiguration.NOTIFICATION_TIMEOUT, DEFAULT_NOTIFICATION_TIMEOUT); - updateConfiguration(c); - } - + this.notificationTimeout = getConfigAs(ZonePlayerConfiguration.class).notificationTimeout; - - super.initialize(); + if (this.notificationTimeout == null) { + this.notificationTimeout = DEFAULT_NOTIFICATION_TIMEOUT; + } } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); - logger.warn("Cannot initalize the zoneplayer. UDN not set."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error-missing-udn"); + logger.debug("Cannot initalize the zoneplayer. UDN not set."); } } @@ -795,7 +792,8 @@ private void updatePlayerState() { if (result.isEmpty()) { if (!ThingStatus.OFFLINE.equals(getThing().getStatus())) { logger.debug("Sonos player {} is not available in local network", getUDN()); - updateStatus(ThingStatus.OFFLINE); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/offline.not-available-on-network [\"" + getUDN() + "\"]"); synchronized (upnpLock) { subscriptionState = new HashMap(); } @@ -877,6 +875,37 @@ protected void updateZoneInfo() { for (String variable : result.keySet()) { this.onValueReceived(variable, result.get(variable), "DeviceProperties"); } + + Map properties = editProperties(); + boolean update = false; + if (StringUtils.isNotEmpty(this.stateMap.get("HardwareVersion")) + && !this.stateMap.get("HardwareVersion").equals(properties.get(Thing.PROPERTY_HARDWARE_VERSION))) { + update = true; + properties.put(Thing.PROPERTY_HARDWARE_VERSION, this.stateMap.get("HardwareVersion")); + } + if (StringUtils.isNotEmpty(this.stateMap.get("DisplaySoftwareVersion")) && !this.stateMap + .get("DisplaySoftwareVersion").equals(properties.get(Thing.PROPERTY_FIRMWARE_VERSION))) { + update = true; + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, this.stateMap.get("DisplaySoftwareVersion")); + } + if (StringUtils.isNotEmpty(this.stateMap.get("SerialNumber")) + && !this.stateMap.get("SerialNumber").equals(properties.get(Thing.PROPERTY_SERIAL_NUMBER))) { + update = true; + properties.put(Thing.PROPERTY_SERIAL_NUMBER, this.stateMap.get("SerialNumber")); + } + if (StringUtils.isNotEmpty(this.stateMap.get("MACAddress")) + && !this.stateMap.get("MACAddress").equals(properties.get(MAC_ADDRESS))) { + update = true; + properties.put(MAC_ADDRESS, this.stateMap.get("MACAddress")); + } + if (StringUtils.isNotEmpty(this.stateMap.get("IPAddress")) + && !this.stateMap.get("IPAddress").equals(properties.get(IP_ADDRESS))) { + update = true; + properties.put(IP_ADDRESS, this.stateMap.get("IPAddress")); + } + if (update) { + updateProperties(properties); + } } public String getCoordinator() { @@ -991,7 +1020,9 @@ else if (isPlayingLineIn(currentURI)) { } } - else if (!currentURI.contains("x-rincon-mp3") && !currentURI.contains("x-sonosapi")) { + else if (isPlayingRadio(currentURI) + || (!currentURI.contains("x-rincon-mp3") && !currentURI.contains("x-sonosapi"))) { + // isPlayingRadio(currentURI) is true for Google Play Music radio or Apple Music radio if (currentTrack != null) { artist = !currentTrack.getAlbumArtist().isEmpty() ? currentTrack.getAlbumArtist() : currentTrack.getCreator(); @@ -1070,7 +1101,9 @@ public SonosMetaData getEnqueuedTransportURIMetaData() { } public String getMACAddress() { - updateZoneInfo(); + if (StringUtils.isEmpty(stateMap.get("MACAddress"))) { + updateZoneInfo(); + } return stateMap.get("MACAddress"); } @@ -1233,8 +1266,8 @@ private Long getResultEntry(Map resultInput, String requestedKey try { result = Long.valueOf(resultInput.get(requestedKey)); } catch (NumberFormatException ex) { - logger.warn("Could not fetch " + requestedKey + " result for type: " + entriesType + " and filter: " - + entriesFilter + ". Using default value '0': " + ex.getMessage(), ex); + logger.warn("Could not fetch {} result for type: {} and filter: {}. Using default value '0': {}", + requestedKey, entriesType, entriesFilter, ex.getMessage(), ex); } return result; @@ -1257,8 +1290,8 @@ protected void saveState() { if (currentURI != null) { - if (isPlayingStream(currentURI)) { - // we are streaming music + if (isPlayingStream(currentURI) || isPlayingRadio(currentURI)) { + // we are streaming music, like tune-in radio or Google Play Music radio SonosMetaData track = getTrackMetadata(); SonosMetaData current = getCurrentURIMetadata(); if (track != null && current != null) { @@ -2209,7 +2242,7 @@ public void playNotificationSoundURI(Command notificationURL) { String currentURI = coordinator.getCurrentURI(); - if (isPlayingStream(currentURI)) { + if (isPlayingStream(currentURI) || isPlayingRadio(currentURI)) { handleRadioStream(currentURI, notificationURL, coordinator); } else if (isPlayingLineIn(currentURI)) { handleLineIn(currentURI, notificationURL, coordinator); @@ -2246,6 +2279,13 @@ private boolean isPlayingStream(String currentURI) { return currentURI.contains(STREAM_URI); } + private boolean isPlayingRadio(String currentURI) { + if (currentURI == null) { + return false; + } + return currentURI.contains(RADIO_URI); + } + private boolean isPlayingLineIn(String currentURI) { if (currentURI == null) { return false; @@ -2388,7 +2428,7 @@ private void waitForFinishedNotification() { // check Sonos state events to determine the end of the notification sound String notificationTitle = stateMap.get("CurrentTitle"); long playstart = System.currentTimeMillis(); - while (System.currentTimeMillis() - playstart < this.notificationTimeout.longValue()) { + while (System.currentTimeMillis() - playstart < this.notificationTimeout.longValue() * 1000) { try { Thread.sleep(50); if (!notificationTitle.equals(stateMap.get("CurrentTitle")) @@ -2407,7 +2447,7 @@ private void waitForTransportState(String state) { while (!stateMap.get("TransportState").equals(state)) { try { Thread.sleep(50); - if (System.currentTimeMillis() - start > this.notificationTimeout.longValue()) { + if (System.currentTimeMillis() - start > this.notificationTimeout.longValue() * 1000) { break; } } catch (InterruptedException e) { @@ -2423,7 +2463,7 @@ private void waitForNotTransportState(String state) { while (stateMap.get("TransportState").equals(state)) { try { Thread.sleep(50); - if (System.currentTimeMillis() - start > this.notificationTimeout.longValue()) { + if (System.currentTimeMillis() - start > this.notificationTimeout.longValue() * 1000) { break; } } catch (InterruptedException e) { diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java index c37ee1e2ca4..6d6ecf3e705 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java @@ -23,6 +23,7 @@ import org.eclipse.smarthome.core.audio.URLAudioStream; import org.eclipse.smarthome.core.audio.UnsupportedAudioFormatException; import org.eclipse.smarthome.core.audio.UnsupportedAudioStreamException; +import org.eclipse.smarthome.core.audio.utils.AudioStreamUtils; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.util.ThingHandlerHelper; @@ -95,9 +96,11 @@ public void process(AudioStream audioStream) logger.warn("Sonos speaker '{}' is not initialized - status is {}", handler.getThing().getUID(), handler.getThing().getStatus()); } else if (AudioFormat.WAV.isCompatible(format)) { - handler.playNotificationSoundURI(new StringType(url + FileAudioStream.WAV_EXTENSION)); + handler.playNotificationSoundURI( + new StringType(url + AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.WAV_EXTENSION)); } else if (AudioFormat.MP3.isCompatible(format)) { - handler.playNotificationSoundURI(new StringType(url + FileAudioStream.MP3_EXTENSION)); + handler.playNotificationSoundURI( + new StringType(url + AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.MP3_EXTENSION)); } else { throw new UnsupportedAudioFormatException("Sonos only supports MP3 or WAV.", format); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java index 2c5d10f3d33..77c0d6d9977 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java +++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java @@ -21,15 +21,19 @@ import org.eclipse.smarthome.core.audio.AudioHTTPServer; import org.eclipse.smarthome.core.audio.AudioSink; import org.eclipse.smarthome.core.net.HttpServiceUtil; -import org.eclipse.smarthome.core.net.NetUtil; +import org.eclipse.smarthome.core.net.NetworkAddressService; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; import org.eclipse.smarthome.io.transport.upnp.UpnpIOService; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +43,7 @@ * * @author Karel Goderis - Initial contribution */ +@Component(service = ThingHandlerFactory.class, immediate = true, name = "binding.sonos", configurationPolicy = ConfigurationPolicy.OPTIONAL) public class SonosHandlerFactory extends BaseThingHandlerFactory { private Logger logger = LoggerFactory.getLogger(SonosHandlerFactory.class); @@ -46,6 +51,7 @@ public class SonosHandlerFactory extends BaseThingHandlerFactory { private UpnpIOService upnpIOService; private DiscoveryServiceRegistry discoveryServiceRegistry; private AudioHTTPServer audioHTTPServer; + private NetworkAddressService networkAddressService; private Map> audioSinkRegistrations = new ConcurrentHashMap<>(); @@ -110,7 +116,7 @@ private String createCallbackUrl() { if (callbackUrl != null) { return callbackUrl; } else { - final String ipAddress = NetUtil.getLocalIpv4HostAddress(); + final String ipAddress = networkAddressService.getPrimaryIpv4HostAddress(); if (ipAddress == null) { logger.warn("No network interface could be found."); return null; @@ -147,6 +153,7 @@ private ThingUID getPlayerUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Conf return thingUID; } + @Reference protected void setUpnpIOService(UpnpIOService upnpIOService) { this.upnpIOService = upnpIOService; } @@ -155,6 +162,7 @@ protected void unsetUpnpIOService(UpnpIOService upnpIOService) { this.upnpIOService = null; } + @Reference protected void setDiscoveryServiceRegistry(DiscoveryServiceRegistry discoveryServiceRegistry) { this.discoveryServiceRegistry = discoveryServiceRegistry; } @@ -163,6 +171,7 @@ protected void unsetDiscoveryServiceRegistry(DiscoveryServiceRegistry discoveryS this.discoveryServiceRegistry = null; } + @Reference protected void setAudioHTTPServer(AudioHTTPServer audioHTTPServer) { this.audioHTTPServer = audioHTTPServer; } @@ -171,4 +180,13 @@ protected void unsetAudioHTTPServer(AudioHTTPServer audioHTTPServer) { this.audioHTTPServer = null; } + @Reference + protected void setNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = networkAddressService; + } + + protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = null; + } + } diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/META-INF/MANIFEST.MF index 9997044e97a..57a8d98c9d4 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Vendor: Eclipse.org/SmartHome Bundle-Version: 0.9.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: org.eclipse.smarthome.binding.tradfri -Import-Package: org.eclipse.smarthome.binding.tradfri, +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.tradfri, org.eclipse.smarthome.binding.tradfri.handler, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, @@ -20,6 +21,5 @@ Import-Package: org.eclipse.smarthome.binding.tradfri, org.osgi.service.device, org.slf4j Require-Bundle: org.junit,org.mockito,org.hamcrest -Export-Package: org.eclipse.smarthome.binding.tradfri.internal;x-internal:=true, - org.eclipse.smarthome.binding.tradfri;uses:="org.eclipse.smarthome.test" +Export-Package: org.eclipse.smarthome.binding.tradfri;uses:="org.eclipse.smarthome.test" diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/about.html b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/build.properties b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/build.properties index acb085918b8..ecedc9a8124 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/build.properties @@ -1,4 +1,5 @@ source.. = src/test/java/ output.. = target/classes bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/org.eclipse.smarthome.binding.tradfri.test.launch b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/org.eclipse.smarthome.binding.tradfri.test.launch new file mode 100644 index 00000000000..f5a972fb698 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/org.eclipse.smarthome.binding.tradfri.test.launch @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/TradfriHandlerTest.java b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/TradfriHandlerTest.java index d1c204c9c1c..be195b45e40 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/TradfriHandlerTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/TradfriHandlerTest.java @@ -44,7 +44,6 @@ public class TradfriHandlerTest extends JavaOSGiTest { public void setUp() { registerService(volatileStorageService); managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class); - assertThat(managedThingProvider, is(notNullValue())); Map properties = new HashMap<>(); properties.put(GatewayConfig.HOST, "1.2.3.4"); diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryParticipantOSGITest.java b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryParticipantOSGITest.java index c22d61a4173..e047b2b8ff6 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryParticipantOSGITest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryParticipantOSGITest.java @@ -22,7 +22,6 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.mdns.discovery.MDNSDiscoveryParticipant; import org.eclipse.smarthome.test.java.JavaOSGiTest; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -34,19 +33,18 @@ */ public class TradfriDiscoveryParticipantOSGITest extends JavaOSGiTest { - MDNSDiscoveryParticipant discoveryParticipant; + private MDNSDiscoveryParticipant discoveryParticipant; @Mock - ServiceInfo tradfriGateway; + private ServiceInfo tradfriGateway; @Mock - ServiceInfo otherDevice; + private ServiceInfo otherDevice; @Before public void setUp() { initMocks(this); discoveryParticipant = getService(MDNSDiscoveryParticipant.class, TradfriDiscoveryParticipant.class); - assertThat(discoveryParticipant, is(notNullValue())); when(tradfriGateway.getType()).thenReturn("_coap._udp.local."); when(tradfriGateway.getName()).thenReturn("gw:12-34-56-78-90-ab"); @@ -61,10 +59,6 @@ public void setUp() { when(otherDevice.getPropertyString("version")).thenReturn("1.1"); } - @After - public void cleanUp() { - } - @Test public void correctSupportedTypes() { assertThat(discoveryParticipant.getSupportedThingTypeUIDs().size(), is(1)); diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryServiceTest.java b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryServiceTest.java index 51a7283d63d..71f657f0784 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryServiceTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri.test/src/test/java/org/eclipse/smarthome/binding/tradfri/discovery/TradfriDiscoveryServiceTest.java @@ -41,12 +41,12 @@ public class TradfriDiscoveryServiceTest { @Mock - TradfriGatewayHandler handler; + private TradfriGatewayHandler handler; - DiscoveryListener listener; - DiscoveryResult discoveryResult; + private DiscoveryListener listener; + private DiscoveryResult discoveryResult; - TradfriDiscoveryService discovery; + private TradfriDiscoveryService discovery; @Before public void setUp() { @@ -88,7 +88,6 @@ public void correctSupportedTypes() { @Test public void validDiscoveryResult() { - String json = "{\"9054\":0,\"9001\":\"LR\",\"5750\":2,\"9002\":1490983446,\"9020\":1491055861,\"9003\":65537,\"9019\":1,\"3\":{\"1\":\"TRADFRI bulb E27 WS opal 980lm\",\"0\":\"IKEA of Sweden\",\"2\":\"\",\"3\":\"1.1.1.1-5.7.2.0\",\"6\":1},\"3311\":[{\"5850\":1,\"5851\":254,\"5707\":0,\"5708\":0,\"5709\":33135,\"5710\":27211,\"9003\":0,\"5711\":0,\"5706\":\"efd275\"}]}"; JsonObject data = new JsonParser().parse(json).getAsJsonObject(); diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/i18n/tradfri_de.properties b/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/i18n/tradfri_de.properties new file mode 100644 index 00000000000..65a82adf2ed --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/i18n/tradfri_de.properties @@ -0,0 +1,29 @@ +# binding +binding.tradfri.name = TRÅDFRI Binding +binding.tradfri.description = Dieses Binding integriert das IKEA TRÅDFRI System. Durch dieses können die TRÅDFRI Lampen und Leuchten gesteuert werden. + +# thing types +thing-type.tradfri.gateway.label = TRÅDFRI Gateway +thing-type.tradfri.gateway.description = IKEA TRÅDFRI Gateway/zentrale Steuereinheit. +thing-type.tradfri.0100.label = Dimmbare Lampe (weiß) +thing-type.tradfri.0100.description = Dimmbare Lampe mit fester Farbtemperatur. +thing-type.tradfri.0220.label = Farbtemperatur Lampe (weiß) +thing-type.tradfri.0220.description = Dimmbare Lampe mit einstellbarer Farbtemperatur. + +# thing type configuration +thing-type.config.tradfri.gateway.host.label = IP Adresse +thing-type.config.tradfri.gateway.host.description = Lokale IP Adresse oder Hostname des TRÅDFRI Gateway. +thing-type.config.tradfri.gateway.port.label = Port +thing-type.config.tradfri.gateway.port.description = Port des TRÅDFRI Gateway. +thing-type.config.tradfri.gateway.code.label = Security Code +thing-type.config.tradfri.gateway.code.description = Security Code zur Authentifizierung am TRÅDFRI Gateway. Befindet sich unterhalb des TRÅDFRI Gateway. +thing-type.config.tradfri.0100.id.label = ID der Lampe +thing-type.config.tradfri.0100.id.description = ID zur Identifikation der Lampe. +thing-type.config.tradfri.0220.id.label = ID der Lampe +thing-type.config.tradfri.0220.id.description = ID zur Identifikation der Lampe. + +# channel types +channel-type.tradfri.brightness.label = Helligkeit +channel-type.tradfri.brightness.description = Ermöglicht die Steuerung der Helligkeit. Ermöglicht ebenfalls die Lampe ein- und auszuschalten. +channel-type.tradfri.color_temperature.label = Farbtemperatur +channel-type.tradfri.color_temperature.description = Ermöglicht die Steuerung der Farbtemperatur. Von Tageslichtweiß (0) bis Warmweiß (100). diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/thing/thing-types.xml b/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/thing/thing-types.xml index 6215f915e0c..3d81d2e9d6f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/thing/thing-types.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri/ESH-INF/thing/thing-types.xml @@ -5,7 +5,8 @@ xsi:schemaLocation="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0 http://eclipse.org/smarthome/schemas/thing-description-1.0.0.xsd"> - + + IKEA TRÃ…DFRI IP Gateway @@ -113,10 +114,7 @@ Dimmer - - The brightness channel allows to control the brightness of a light. - It is also possible to switch the light on and off. - + Control the brightness and switch the light on and off. DimmableLight Lighting @@ -126,7 +124,7 @@ Dimmer - Allows to control the color temperature of light. + Control the color temperature of the light. ColorLight diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.tradfri/META-INF/MANIFEST.MF index 4b3ff06102e..6698c3fef1e 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Import-Package: org.eclipse.californium.scandium, org.eclipse.californium.scandium.config, org.eclipse.californium.scandium.dtls.pskstore, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.tradfri, org.eclipse.smarthome.binding.tradfri.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/handler/TradfriLightHandler.java b/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/handler/TradfriLightHandler.java index 6c937ed7dcc..970f1af61e8 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/handler/TradfriLightHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/handler/TradfriLightHandler.java @@ -181,7 +181,7 @@ private void handleBrightnessCommand(Command command) { } else if (command instanceof OnOffType) { setState(((OnOffType) command)); } else if (command instanceof IncreaseDecreaseType) { - if (state != null) { + if (state != null && state.getBrightness() != null) { int current = state.getBrightness().intValue(); if (IncreaseDecreaseType.INCREASE.equals(command)) { setBrightness(new PercentType(Math.min(current + STEP, PercentType.HUNDRED.intValue()))); @@ -200,7 +200,7 @@ private void handleColorTemperatureCommand(Command command) { if (command instanceof PercentType) { setColorTemperature((PercentType) command); } else if (command instanceof IncreaseDecreaseType) { - if (state != null) { + if (state != null && state.getColorTemperature() != null) { int current = state.getColorTemperature().intValue(); if (IncreaseDecreaseType.INCREASE.equals(command)) { setColorTemperature(new PercentType(Math.min(current + STEP, PercentType.HUNDRED.intValue()))); diff --git a/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/internal/discovery/TradfriDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/internal/discovery/TradfriDiscoveryService.java index af21b1da194..54388adc0f0 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/internal/discovery/TradfriDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.tradfri/src/main/java/org/eclipse/smarthome/binding/tradfri/internal/discovery/TradfriDiscoveryService.java @@ -104,7 +104,7 @@ public void onUpdate(String instanceId, JsonObject data) { Map properties = new HashMap<>(1); properties.put("id", id); if (deviceInfo.get(DEVICE_VENDOR) != null) { - properties.put("vendor", deviceInfo.get(DEVICE_VENDOR)); + properties.put("vendor", deviceInfo.get(DEVICE_VENDOR).getAsString()); } logger.debug("Adding device {} to inbox", thingId); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingId).withBridge(bridge) diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground.properties b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground.properties new file mode 100644 index 00000000000..e5d426c1c8f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground.properties @@ -0,0 +1,9 @@ +# Thing status description +offline.comm-error-invalid-api-key = The API key is invalid. +offline.comm-error-running-request = An error occurred while running the Weather Underground request. +offline.comm-error-parsing-response = An error occurred while parsing the response to the Weather Underground request. +offline.comm-error-response = An error was detected in the response to the Weather Underground request; please check the logs for more details. +offline.conf-error-missing-apikey = The "API key" parameter must be configured. +offline.conf-error-missing-location = The "location" parameter must be configured. +offline.conf-error-syntax-language = The value of the "language" parameter must contain 2 letters. +offline.conf-error-min-refresh = The minimum value of the "refresh interval" parameter must be 5 minutes. diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground_fr.properties b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground_fr.properties new file mode 100644 index 00000000000..4fc64d58a73 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/i18n/weatherunderground_fr.properties @@ -0,0 +1,199 @@ +# binding +binding.weatherunderground.name = Extension WeatherUnderground +binding.weatherunderground.description = L'extension Weather Underground interroge le service Weather Underground pour récupérer des données météo. + +# thing types +thing-type.weatherunderground.weather.label = Informations météo +thing-type.weatherunderground.weather.description = Présente diverses données météo fournies par le service Weather Underground. + +# thing type configuration +thing-type.config.weatherunderground.weather.apikey.label = Clé d'accès +thing-type.config.weatherunderground.weather.apikey.description = La clé d'accès au service Weather Underground. +thing-type.config.weatherunderground.weather.location.label = Emplacement des données météo +thing-type.config.weatherunderground.weather.location.description = Plusieurs syntaxes sont possibles. Merci de consulter la documentation de l'extension pour plus d'information. +thing-type.config.weatherunderground.weather.language.label = Langue +thing-type.config.weatherunderground.weather.language.description = La langue à utiliser par le service Weather Underground. +thing-type.config.weatherunderground.weather.language.option.LI = Anglais britannique +thing-type.config.weatherunderground.weather.language.option.NL = Néerlandais +thing-type.config.weatherunderground.weather.language.option.EN = Anglais +thing-type.config.weatherunderground.weather.language.option.FR = Français +thing-type.config.weatherunderground.weather.language.option.FC = Français canadien +thing-type.config.weatherunderground.weather.language.option.DL = Allemand +thing-type.config.weatherunderground.weather.language.option.IT = Italien +thing-type.config.weatherunderground.weather.language.option.BR = Portugais +thing-type.config.weatherunderground.weather.language.option.RU = Russe +thing-type.config.weatherunderground.weather.language.option.SP = Espagnol +thing-type.config.weatherunderground.weather.refresh.label = Fréquence de rafraîchissement +thing-type.config.weatherunderground.weather.refresh.description = La fréquence de rafraîchissement des données en minutes. + +# channel group types +channel-group-type.weatherunderground.current.label = Météo actuelle +channel-group-type.weatherunderground.current.description = La météo actuelle. +channel-group-type.weatherunderground.forecast.label = Météo prévue +channel-group-type.weatherunderground.forecast.description = La météo prévue. + +# channel groups +thing-type.weatherunderground.weather.group.forecastToday.label = Météo d'aujourd'hui +thing-type.weatherunderground.weather.group.forecastToday.description = La météo prévue aujourd'hui. +thing-type.weatherunderground.weather.group.forecastTomorrow.label = Météo de demain +thing-type.weatherunderground.weather.group.forecastTomorrow.description = La météo prévue demain. +thing-type.weatherunderground.weather.group.forecastDay2.label = Météo dans 2 jours +thing-type.weatherunderground.weather.group.forecastDay2.description = La météo prévue dans 2 jours. +thing-type.weatherunderground.weather.group.forecastDay3.label = Météo dans 3 jours +thing-type.weatherunderground.weather.group.forecastDay3.description = La météo prévue dans 3 jours. +thing-type.weatherunderground.weather.group.forecastDay4.label = Météo dans 4 jours +thing-type.weatherunderground.weather.group.forecastDay4.description = La météo prévue dans 4 jours. +thing-type.weatherunderground.weather.group.forecastDay5.label = Météo dans 5 jours +thing-type.weatherunderground.weather.group.forecastDay5.description = La météo prévue dans 5 jours. +thing-type.weatherunderground.weather.group.forecastDay6.label = Météo dans 6 jours +thing-type.weatherunderground.weather.group.forecastDay6.description = La météo prévue dans 6 jours. +thing-type.weatherunderground.weather.group.forecastDay7.label = Météo dans 7 jours +thing-type.weatherunderground.weather.group.forecastDay7.description = La météo prévue dans 7 jours. +thing-type.weatherunderground.weather.group.forecastDay8.label = Météo dans 8 jours +thing-type.weatherunderground.weather.group.forecastDay8.description = La météo prévue dans 8 jours. +thing-type.weatherunderground.weather.group.forecastDay9.label = Météo dans 9 jours +thing-type.weatherunderground.weather.group.forecastDay9.description = La météo prévue dans 9 jours. + +# channel types +channel-type.weatherunderground.location.label = Emplacement d'observation +channel-type.weatherunderground.location.description = L'emplacement d'observation de la météo. +channel-type.weatherunderground.stationId.label = ID station +channel-type.weatherunderground.stationId.description = L'identifiant de la station météo. +channel-type.weatherunderground.observationTime.label = Heure d'observation +channel-type.weatherunderground.observationTime.description = La date et l'heure d'observation de la météo. +channel-type.weatherunderground.forecastTime.label = Heure des prévisions +channel-type.weatherunderground.forecastTime.description = La date et l'heure des prévisions météo. +channel-type.weatherunderground.currentConditions.label = Conditions actuelles +channel-type.weatherunderground.currentConditions.description = Les conditions météo actuelles. +channel-type.weatherunderground.forecastConditions.label = Conditions prévues +channel-type.weatherunderground.forecastConditions.description = Les conditions météo prévues. +channel-type.weatherunderground.temperature.label = Température +channel-type.weatherunderground.temperature.description = La température actuelle. +channel-type.weatherunderground.minTemperature.label = Température minimale +channel-type.weatherunderground.minTemperature.description = La température minimale. +channel-type.weatherunderground.maxTemperature.label = Température maximale +channel-type.weatherunderground.maxTemperature.description = La température maximale. +channel-type.weatherunderground.dewPoint.label = Température du point de rosée +channel-type.weatherunderground.dewPoint.description = La température du point de rosée. +channel-type.weatherunderground.heatIndex.label = Indice de chaleur +channel-type.weatherunderground.heatIndex.description = L'indice de chaleur. +channel-type.weatherunderground.windChill.label = Température de refroidissement du vent +channel-type.weatherunderground.windChill.description = La température de refroidissement du vent. +channel-type.weatherunderground.feelingTemperature.label = Température ressentie +channel-type.weatherunderground.feelingTemperature.description = La température ressentie. +channel-type.weatherunderground.relativeHumidity.label = Humidité relative +channel-type.weatherunderground.relativeHumidity.description = l'humidité relative en %. +channel-type.weatherunderground.windDirection.label = Direction du vent +channel-type.weatherunderground.windDirection.description = La direction du vent. +channel-type.weatherunderground.maxWindDirection.label = Direction du vent max +channel-type.weatherunderground.maxWindDirection.description = La direction du vent le plus fort. +channel-type.weatherunderground.averageWindDirection.label = Direction moyenne du vent +channel-type.weatherunderground.averageWindDirection.description = La direction moyenne du vent. +channel-type.weatherunderground.windDirection-degrees.label = Direction du vent (°) +channel-type.weatherunderground.windDirection-degrees.description = La direction du vent en degrés. +channel-type.weatherunderground.maxWindDirection-degrees.label = Direction du vent max (°) +channel-type.weatherunderground.maxWindDirection-degrees.description = La direction du vent le plus fort en degrés. +channel-type.weatherunderground.averageWindDirection-degrees.label = Direction moyenne du vent (°) +channel-type.weatherunderground.averageWindDirection-degrees.description = La direction moyenne du vent en degrés. +channel-type.weatherunderground.windSpeed.label = Vitesse du vent +channel-type.weatherunderground.windSpeed.description = La vitesse du vent. +channel-type.weatherunderground.maxWindSpeed.label = Vitesse max du vent +channel-type.weatherunderground.maxWindSpeed.description = La vitesse maximum du vent. +channel-type.weatherunderground.averageWindSpeed.label = Vitesse moyenne du vent +channel-type.weatherunderground.averageWindSpeed.description = La vitesse moyenne du vent. +channel-type.weatherunderground.windGust.label = Vitesse du vent en rafale +channel-type.weatherunderground.windGust.description = La vitesse du vent en rafale. +channel-type.weatherunderground.pressure.label = Pression atmosphérique +channel-type.weatherunderground.pressure.description = La pression atmosphérique. +channel-type.weatherunderground.visibility.label = Distance de visibilité +channel-type.weatherunderground.visibility.description = La distance de visibilité. +channel-type.weatherunderground.solarRadiation.label = Rayonnement solaire +channel-type.weatherunderground.solarRadiation.description = L'énergie rayonnante émise par le soleil. +channel-type.weatherunderground.UVIndex.label = Indice UV +channel-type.weatherunderground.UVIndex.description = L'indice UV. +channel-type.weatherunderground.rainDay.label = Chutes de pluie dans la journée +channel-type.weatherunderground.rainDay.description = Les chutes de pluie sur toute la journée. +channel-type.weatherunderground.rainHour.label = Chutes de pluie dans l'heure +channel-type.weatherunderground.rainHour.description = Les chutes de pluie sur la dernière heure. +channel-type.weatherunderground.snow.label = Chutes de neige dans la journée +channel-type.weatherunderground.snow.description = Les chutes de neige sur toute la journée. +channel-type.weatherunderground.probaPrecipitation.label = Probabilité de précipitation +channel-type.weatherunderground.probaPrecipitation.description = La probabilité de précipitation en %. +channel-type.weatherunderground.icon.label = Icône météo +channel-type.weatherunderground.icon.description = L'icône représentant les conditions météo. + +# channel type configuration +channel-type.config.weatherunderground.temperature.SourceUnit.label = Unité de température +channel-type.config.weatherunderground.temperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour la température actuelle. +channel-type.config.weatherunderground.temperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.temperature.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.minTemperature.SourceUnit.label = Unité de température minimale +channel-type.config.weatherunderground.minTemperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Undergroundde pour la température minimale . +channel-type.config.weatherunderground.minTemperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.minTemperature.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.maxTemperature.SourceUnit.label = Unité de température maximale +channel-type.config.weatherunderground.maxTemperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Undergroundde pour la température maximale. +channel-type.config.weatherunderground.maxTemperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.maxTemperature.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.dewPoint.SourceUnit.label = Unité de température du point de rosée +channel-type.config.weatherunderground.dewPoint.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour le point de rosée. +channel-type.config.weatherunderground.dewPoint.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.dewPoint.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.heatIndex.SourceUnit.label = Unité de température de l'indice de chaleur +channel-type.config.weatherunderground.heatIndex.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour l'indice de chaleur. +channel-type.config.weatherunderground.heatIndex.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.heatIndex.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.windChill.SourceUnit.label = Unité de température du vent +channel-type.config.weatherunderground.windChill.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour le refroidissement du vent. +channel-type.config.weatherunderground.windChill.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.windChill.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.feelingTemperature.SourceUnit.label = Unité de température ressentie +channel-type.config.weatherunderground.feelingTemperature.SourceUnit.description = Choix de l'unité de température fournie par le service Weather Underground pour la température ressentie. +channel-type.config.weatherunderground.feelingTemperature.SourceUnit.option.C = Degrés Celsius +channel-type.config.weatherunderground.feelingTemperature.SourceUnit.option.F = Degrés Fahrenheit +channel-type.config.weatherunderground.windSpeed.SourceUnit.label = Unité de vitesse du vent +channel-type.config.weatherunderground.windSpeed.SourceUnit.description = Choix de l'unité de vitesse fournie par le service Weather Underground pour le vent. +channel-type.config.weatherunderground.windSpeed.SourceUnit.option.kmh = Kilomètres par heure +channel-type.config.weatherunderground.windSpeed.SourceUnit.option.mph = Miles par heure +channel-type.config.weatherunderground.maxWindSpeed.SourceUnit.label = Unité de vitesse du vent max +channel-type.config.weatherunderground.maxWindSpeed.SourceUnit.description = Choix de l'unité de vitesse fournie par le service Weather Underground pour le vent maximum. +channel-type.config.weatherunderground.maxWindSpeed.SourceUnit.option.kmh = Kilomètres par heure +channel-type.config.weatherunderground.maxWindSpeed.SourceUnit.option.mph = Miles par heure +channel-type.config.weatherunderground.averageWindSpeed.SourceUnit.label = Unité de vitesse du vent moyen +channel-type.config.weatherunderground.averageWindSpeed.SourceUnit.description = Choix de l'unité de vitesse fournie par le service Weather Underground pour le vent moyen. +channel-type.config.weatherunderground.averageWindSpeed.SourceUnit.option.kmh = Kilomètres par heure +channel-type.config.weatherunderground.averageWindSpeed.SourceUnit.option.mph = Miles par heure +channel-type.config.weatherunderground.windGust.SourceUnit.label = Unité de vitesse du vent en rafale +channel-type.config.weatherunderground.windGust.SourceUnit.description = Choix de l'unité de vitesse fournie par le service Weather Underground pour le vent en rafale. +channel-type.config.weatherunderground.windGust.SourceUnit.option.kmh = Kilomètres par heure +channel-type.config.weatherunderground.windGust.SourceUnit.option.mph = Miles par heure +channel-type.config.weatherunderground.pressure.SourceUnit.label = Unité de pression atmosphérique +channel-type.config.weatherunderground.pressure.SourceUnit.description = Choix de l'unité de pression atmosphérique fournie par le service Weather Underground. +channel-type.config.weatherunderground.pressure.SourceUnit.option.hPa = Hectopascal +channel-type.config.weatherunderground.pressure.SourceUnit.option.inHg = Pouce de mercure +channel-type.config.weatherunderground.visibility.SourceUnit.label = Unité de distance de visibilité +channel-type.config.weatherunderground.visibility.SourceUnit.description = Choix de l'unité de distance fournie par le service Weather Underground pour la visibilité. +channel-type.config.weatherunderground.visibility.SourceUnit.option.km = Kilomètres +channel-type.config.weatherunderground.visibility.SourceUnit.option.mi = Miles +channel-type.config.weatherunderground.rainDay.SourceUnit.label = Unité de quantité de chutes de pluie +channel-type.config.weatherunderground.rainDay.SourceUnit.description = Choix de l'unité de quantité de chutes de pluie fournie par le service Weather Underground. +channel-type.config.weatherunderground.rainDay.SourceUnit.option.mm = Millimètres +channel-type.config.weatherunderground.rainDay.SourceUnit.option.in = Pouces +channel-type.config.weatherunderground.rainHour.SourceUnit.label = Unité de quantité de chutes de pluie +channel-type.config.weatherunderground.rainHour.SourceUnit.description = Choix de l'unité de quantité de chutes de pluie fournie par le service Weather Underground. +channel-type.config.weatherunderground.rainHour.SourceUnit.option.mm = Millimètres +channel-type.config.weatherunderground.rainHour.SourceUnit.option.in = Pouces +channel-type.config.weatherunderground.snow.SourceUnit.label = Unité de quantité de chutes de neige +channel-type.config.weatherunderground.snow.SourceUnit.description = Choix de l'unité de quantité de chutes de neige fournie par le service Weather Underground. +channel-type.config.weatherunderground.snow.SourceUnit.option.cm = Centimètres +channel-type.config.weatherunderground.snow.SourceUnit.option.in = Pouces + +# Thing status description +offline.comm-error-invalid-api-key = La clé d''accès est invalide. +offline.comm-error-running-request = Une erreur est survenue lors de l''exécution de la requête Weather Underground. +offline.comm-error-parsing-response = Une erreur est survenue lors de l''analyse de la réponse à la requête Weather Underground. +offline.comm-error-response = Une erreur a été détectée dans la réponse à la requête Weather Underground; merci de consulter les logs pour plus de détails. +offline.conf-error-missing-apikey = Le paramètre "clé d''accès" doit être configuré. +offline.conf-error-missing-location = Le paramètre "emplacement des données météo" doit être configuré. +offline.conf-error-syntax-language = La valeur du paramètre "Langue" doit contenir 2 lettres. +offline.conf-error-min-refresh = La valeur minimale du paramètre "fréquence de rafraîchissement" doit être de 5 minutes. diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/thing/thing-types.xml b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/thing/thing-types.xml index 929e26dce53..ce6643a2f71 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/thing/thing-types.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/ESH-INF/thing/thing-types.xml @@ -150,7 +150,7 @@ - + Specifies the refresh interval in minutes. 30 @@ -253,7 +253,7 @@ Number - Temperature + Current temperature Temperature diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/META-INF/MANIFEST.MF index a69dc099a93..e7207b70646 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/META-INF/MANIFEST.MF @@ -10,9 +10,11 @@ Import-Package: com.google.common.collect, com.google.gson, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.weatherunderground, org.eclipse.smarthome.binding.weatherunderground.handler, org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.i18n, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.thing, diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b81c7954b78 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/.gitignore @@ -0,0 +1 @@ +*.xml \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/WeatherUndergroundHandlerFactory.xml b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/WeatherUndergroundHandlerFactory.xml deleted file mode 100644 index 9d573505955..00000000000 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/OSGI-INF/WeatherUndergroundHandlerFactory.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/README.md b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/README.md index 087c973ee21..eb1eab6ddae 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/README.md +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/README.md @@ -20,7 +20,11 @@ There is exactly one supported thing type, which represents the weather informat ## Discovery -There is no discovery implemented. You have to create your things manually. +If a system location is set, "Local Weather" will be automatically discovered for this location. + +If the system location is changed, the background discovery updates the configuration of "Local Weather" automatically. + +After adding this discovered thing, you will have to set the correct API key. ## Binding Configuration @@ -35,7 +39,7 @@ The thing has a few configuration parameters: | apikey | API key to access the Weather Underground service. Mandatory. | | location | Location to be considered by the Weather Underground service. Mandatory. | | language | Language to be used by the Weather Underground service. Optional, the default is to use the language from the system locale. | -| refresh | Refresh interval in minutes. Optional, the default value is 30 minutes. | +| refresh | Refresh interval in minutes. Optional, the default value is 30 minutes and the minimum value is 5 minutes. | For the location parameter, different syntaxes are possible: @@ -72,7 +76,7 @@ The weather information that is retrieved is available as these channels: | Current | windChill | Number | Wind chill temperature | SourceUnit: "C" for degrees Celsius or "F" for degrees Fahrenheit; default is "C" | | Current | feelingTemperature | Number | Feeling temperature | SourceUnit: "C" for degrees Celsius or "F" for degrees Fahrenheit; default is "C" | | Current | visibility | Number | Visibility | SourceUnit: "km" or "mi"; default is "km" | -| Current | sorlarRadiation | Number | Solar radiation in W/m2 | | +| Current | solarRadiation | Number | Solar radiation in W/m2 | | | Current | UVIndex | Number | UV Index | | | Current | precipitationDay | Number | Rain fall during the day | SourceUnit: "mm" or "in"; default is "mm" | | Current | precipitationHour | Number | Rain fall during the last hour | SourceUnit: "mm" or "in"; default is "mm" | @@ -99,7 +103,7 @@ The weather information that is retrieved is available as these channels: demo.things: ``` -Thing weatherunderground:weather:CDG "Météo Paris CDG" [ apikey="XXXXXXXXXXXX", location="CDG", language="FR" ] { +Thing weatherunderground:weather:CDG "Météo Paris CDG" [ apikey="XXXXXXXXXXXX", location="CDG", language="FR", refresh=15 ] { Channels: Type temperature : current#temperature [ SourceUnit="C" ] Type windSpeed : current#windSpeed [ SourceUnit="kmh" ] diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/WeatherUndergroundBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/WeatherUndergroundBindingConstants.java index f92fe06f4d2..07ea6bde894 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/WeatherUndergroundBindingConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/WeatherUndergroundBindingConstants.java @@ -19,6 +19,8 @@ public class WeatherUndergroundBindingConstants { public static final String BINDING_ID = "weatherunderground"; + public static final String LOCAL = "local"; + // List all Thing Type UIDs, related to the WeatherUnderground Binding public final static ThingTypeUID THING_TYPE_WEATHER = new ThingTypeUID(BINDING_ID, "weather"); diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/handler/WeatherUndergroundHandler.java b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/handler/WeatherUndergroundHandler.java index e9a85765680..8b6b4f0293d 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/handler/WeatherUndergroundHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/handler/WeatherUndergroundHandler.java @@ -87,24 +87,29 @@ public void initialize() { boolean validConfig = true; String errors = ""; + String statusDescr = null; if (StringUtils.trimToNull(config.apikey) == null) { errors += " Parameter 'apikey' must be configured."; + statusDescr = "@text/offline.conf-error-missing-apikey"; validConfig = false; } if (StringUtils.trimToNull(config.location) == null) { errors += " Parameter 'location' must be configured."; + statusDescr = "@text/offline.conf-error-missing-location"; validConfig = false; } if (config.language != null) { String lang = StringUtils.trimToEmpty(config.language); if (lang.length() != 2) { errors += " Parameter 'language' must be 2 letters."; + statusDescr = "@text/offline.conf-error-syntax-language"; validConfig = false; } } if (config.refresh != null && config.refresh < 5) { errors += " Parameter 'refresh' must be at least 5 minutes."; + statusDescr = "@text/offline.conf-error-min-refresh"; validConfig = false; } errors = errors.trim(); @@ -113,7 +118,7 @@ public void initialize() { startAutomaticRefresh(); } else { logger.debug("Disabling thing '{}': {}", getThing().getUID(), errors); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errors); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, statusDescr); } } @@ -141,7 +146,7 @@ public void run() { WeatherUndergroundConfiguration config = getConfigAs(WeatherUndergroundConfiguration.class); int period = (config.refresh != null) ? config.refresh.intValue() : DEFAULT_REFRESH_PERIOD; - refreshJob = scheduler.scheduleAtFixedRate(runnable, 0, period, TimeUnit.MINUTES); + refreshJob = scheduler.scheduleWithFixedDelay(runnable, 0, period, TimeUnit.MINUTES); } } @@ -171,9 +176,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { * @param channelId the id identifying the channel to be updated */ private void updateChannel(String channelId) { - if (isLinked(channelId)) { + Channel channel = getThing().getChannel(channelId); + if (channel != null && isLinked(channelId)) { // Get the source unit property from the channel when defined - String sourceUnit = (String) getThing().getChannel(channelId).getConfiguration() + String sourceUnit = (String) channel.getConfiguration() .get(WeatherUndergroundBindingConstants.PROPERTY_SOURCE_UNIT); if (sourceUnit == null) { sourceUnit = ""; @@ -261,6 +267,7 @@ private WeatherUndergroundJsonData getWeatherData(Set features, String l boolean resultOk = false; String error = null; String errorDetail = null; + String statusDescr = null; try { @@ -298,6 +305,10 @@ private WeatherUndergroundJsonData getWeatherData(Set features, String l } else if (result.getResponse() == null) { errorDetail = "missing response sub-object"; } else if (result.getResponse().getErrorDescription() != null) { + if ("keynotfound".equals(result.getResponse().getErrorType())) { + error = "API key has to be fixed"; + statusDescr = "@text/offline.comm-error-invalid-api-key"; + } errorDetail = result.getResponse().getErrorDescription(); } else { resultOk = true; @@ -310,19 +321,22 @@ private WeatherUndergroundJsonData getWeatherData(Set features, String l errorDetail = "missing forecast sub-object"; } else if (feature.equals(FEATURE_GEOLOOKUP) && result.getLocation() == null) { resultOk = false; - errorDetail = "missing forecast sub-object"; + errorDetail = "missing location sub-object"; } } } - if (!resultOk) { + if (!resultOk && error == null) { error = "Error in Weather Underground response"; + statusDescr = "@text/offline.comm-error-response"; } } catch (IOException e) { error = "Error running Weather Underground request"; errorDetail = e.getMessage(); + statusDescr = "@text/offline.comm-error-running-request"; } catch (JsonSyntaxException e) { error = "Error parsing Weather Underground response"; errorDetail = e.getMessage(); + statusDescr = "@text/offline.comm-error-parsing-response"; } // Update the thing status @@ -330,7 +344,7 @@ private WeatherUndergroundJsonData getWeatherData(Set features, String l updateStatus(ThingStatus.ONLINE); } else { logger.debug("{}: {}", error, errorDetail); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, error); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, statusDescr); } return resultOk ? result : null; diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/WeatherUndergroundHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/WeatherUndergroundHandlerFactory.java index f7f68d6cfbe..be8d5f4fd81 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/WeatherUndergroundHandlerFactory.java +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/WeatherUndergroundHandlerFactory.java @@ -18,6 +18,9 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link WeatherUndergroundHandlerFactory} is responsible for creating things and thing @@ -25,10 +28,12 @@ * * @author Laurent Garnier - Initial contribution */ +@Component(service = ThingHandlerFactory.class, immediate = true) public class WeatherUndergroundHandlerFactory extends BaseThingHandlerFactory { private LocaleProvider localeProvider; + @Reference protected void setLocaleProvider(final LocaleProvider localeProvider) { this.localeProvider = localeProvider; } diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/config/WeatherUndergroundConfiguration.java b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/config/WeatherUndergroundConfiguration.java index a7330237e13..d30c6256616 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/config/WeatherUndergroundConfiguration.java +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/config/WeatherUndergroundConfiguration.java @@ -15,6 +15,8 @@ */ public class WeatherUndergroundConfiguration { + public static final String LOCATION = "location"; + public String apikey; public String location; public String language; diff --git a/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/discovery/WeatherUndergroundDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/discovery/WeatherUndergroundDiscoveryService.java new file mode 100644 index 00000000000..2d38baa77ce --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.weatherunderground/src/main/java/org/eclipse/smarthome/binding/weatherunderground/internal/discovery/WeatherUndergroundDiscoveryService.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2014-2017 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.eclipse.smarthome.binding.weatherunderground.internal.discovery; + +import static org.eclipse.smarthome.binding.weatherunderground.WeatherUndergroundBindingConstants.*; +import static org.eclipse.smarthome.binding.weatherunderground.internal.config.WeatherUndergroundConfiguration.LOCATION; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.i18n.LocationProvider; +import org.eclipse.smarthome.core.library.types.PointType; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WeatherUndergroundDiscoveryService} creates things based on the configured location. + * + * @author Laurent Garnier - Initial Contribution + */ +@Component(service = DiscoveryService.class, immediate = true) +public class WeatherUndergroundDiscoveryService extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(WeatherUndergroundDiscoveryService.class); + + private static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_WEATHER); + private static final int DISCOVER_TIMEOUT_SECONDS = 10; + private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60; + + private LocationProvider locationProvider; + private ScheduledFuture discoveryJob; + private PointType previousLocation; + + /** + * Creates a WeatherUndergroundDiscoveryService with enabled autostart. + */ + public WeatherUndergroundDiscoveryService() { + super(SUPPORTED_THING_TYPES, DISCOVER_TIMEOUT_SECONDS, true); + } + + @Override + protected void startScan() { + logger.debug("Starting Weather Underground discovery scan"); + PointType location = locationProvider.getLocation(); + if (location == null) { + logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results"); + return; + } + createResults(location); + } + + @Override + protected void startBackgroundDiscovery() { + if (discoveryJob == null) { + discoveryJob = scheduler.scheduleWithFixedDelay(() -> { + PointType currentLocation = locationProvider.getLocation(); + if (!Objects.equals(currentLocation, previousLocation)) { + logger.debug("Location has been changed from {} to {}: Creating new discovery results", + previousLocation, currentLocation); + createResults(currentLocation); + previousLocation = currentLocation; + } + }, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS); + logger.debug("Scheduled Weather Underground location-changed job every {} seconds", + LOCATION_CHANGED_CHECK_INTERVAL); + } + } + + @Override + protected void stopBackgroundDiscovery() { + logger.debug("Stopping Weather Underground device background discovery"); + if (discoveryJob != null && !discoveryJob.isCancelled()) { + if (discoveryJob.cancel(true)) { + discoveryJob = null; + logger.debug("Stopped Weather Underground device background discovery"); + } + } + } + + public void createResults(PointType location) { + ThingUID localWeatherThing = new ThingUID(THING_TYPE_WEATHER, LOCAL); + Map properties = new HashMap<>(3); + properties.put(LOCATION, String.format("%s,%s", location.getLatitude(), location.getLongitude())); + thingDiscovered(DiscoveryResultBuilder.create(localWeatherThing).withLabel("Local Weather") + .withProperties(properties).build()); + } + + @Reference + protected void setLocationProvider(LocationProvider locationProvider) { + this.locationProvider = locationProvider; + } + + protected void unsetLocationProvider(LocationProvider locationProvider) { + this.locationProvider = null; + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/META-INF/MANIFEST.MF index 89f69ffbb86..554b4bd0a7e 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo.test/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/META-INF/MANIFEST.MF @@ -7,30 +7,31 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: org.eclipse.smarthome.binding.wemo Import-Package: groovy.json, groovy.lang, - groovy.xml, groovy.util, + groovy.xml, + javax.servlet, + javax.servlet.http, + junit.framework, org.apache.commons.lang, org.codehaus.groovy.reflection, org.codehaus.groovy.runtime, org.codehaus.groovy.runtime.callsite, org.codehaus.groovy.runtime.typehandling, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.config.discovery.inbox, org.eclipse.smarthome.core.items, + org.eclipse.smarthome.core.library.items, org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.config.discovery.inbox, + org.eclipse.smarthome.core.thing.binding.builder, org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.core.library.items, + org.eclipse.smarthome.core.thing.type, org.eclipse.smarthome.test, org.eclipse.smarthome.test.storage, - org.osgi.service.cm, - org.osgi.service.http, org.hamcrest;core=split, org.junit;version="4.0.0", org.junit.runner, org.junit.runners, org.jupnp, org.jupnp.mock, - javax.servlet, - javax.servlet.http, - junit.framework + org.osgi.service.cm, + org.osgi.service.http diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo.test/about.html b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo.test/build.properties b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/build.properties index 6434d1ace5e..642ffee0042 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo.test/build.properties +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo.test/build.properties @@ -1,4 +1,5 @@ source.. = src/main/groovy/ output.. = target/classes/ bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.wemo/META-INF/MANIFEST.MF index afb0003d5bf..fbb88129d75 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ClassPath: . Import-Package: com.google.common.collect, org.apache.commons.io, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.wemo, org.eclipse.smarthome.binding.wemo.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/README.md b/extensions/binding/org.eclipse.smarthome.binding.wemo/README.md index 11b51055525..ed9df797455 100755 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/README.md +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/README.md @@ -19,9 +19,11 @@ The binding does not need any special configuration For manual Thing configuration, one needs to know the UUID of a certain WeMo device. In the thing file, this looks e.g. like + ``` wemo:socket:Switch1 [udn="Socket-1_0-221242K11xxxxx"] ``` + For a WeMo Link bridge and paired LED Lights, please use the following Thing definition ``` diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/discovery/WemoLinkDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/discovery/WemoLinkDiscoveryService.java index 1fc3aaad176..5de8cc67552 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/discovery/WemoLinkDiscoveryService.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/discovery/WemoLinkDiscoveryService.java @@ -166,29 +166,29 @@ protected void startScan() { NodeList deviceIndex = element.getElementsByTagName("DeviceIndex"); Element line = (Element) deviceIndex.item(0); - logger.trace("DeviceIndex: " + getCharacterDataFromElement(line)); + logger.trace("DeviceIndex: {}", getCharacterDataFromElement(line)); NodeList deviceID = element.getElementsByTagName("DeviceID"); line = (Element) deviceID.item(0); String endDeviceID = getCharacterDataFromElement(line); - logger.trace("DeviceID: " + endDeviceID); + logger.trace("DeviceID: {}", endDeviceID); NodeList friendlyName = element.getElementsByTagName("FriendlyName"); line = (Element) friendlyName.item(0); String endDeviceName = getCharacterDataFromElement(line); - logger.trace("FriendlyName: " + endDeviceName); + logger.trace("FriendlyName: {}", endDeviceName); NodeList vendor = element.getElementsByTagName("Manufacturer"); line = (Element) vendor.item(0); String endDeviceVendor = getCharacterDataFromElement(line); - logger.trace("Manufacturer: " + endDeviceVendor); + logger.trace("Manufacturer: {}", endDeviceVendor); NodeList model = element.getElementsByTagName("ModelCode"); line = (Element) model.item(0); String endDeviceModelID = getCharacterDataFromElement(line); endDeviceModelID = endDeviceModelID.replaceAll(NORMALIZE_ID_REGEX, "_"); - logger.trace("ModelCode: " + endDeviceModelID); + logger.trace("ModelCode: {}", endDeviceModelID); if (SUPPORTED_THING_TYPES.contains(new ThingTypeUID(BINDING_ID, endDeviceModelID))) { logger.debug("Discovered a WeMo LED Light thing with ID '{}'", endDeviceID); @@ -213,11 +213,11 @@ protected void startScan() { } else { logger.debug("Discovered an unsupported device :"); - logger.debug("DeviceIndex : " + getCharacterDataFromElement(line)); - logger.debug("DeviceID : " + endDeviceID); - logger.debug("FriendlyName: " + endDeviceName); - logger.debug("Manufacturer: " + endDeviceVendor); - logger.debug("ModelCode : " + endDeviceModelID); + logger.debug("DeviceIndex : {}", getCharacterDataFromElement(line)); + logger.debug("DeviceID : {}", endDeviceID); + logger.debug("FriendlyName: {}", endDeviceName); + logger.debug("Manufacturer: {}", endDeviceVendor); + logger.debug("ModelCode : {}", endDeviceModelID); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoBridgeHandler.java index fe1f2a24b91..1b9cd7b86dd 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoBridgeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoBridgeHandler.java @@ -15,6 +15,7 @@ import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; @@ -46,7 +47,7 @@ public void initialize() { if (configuration.get(UDN) != null) { logger.trace("Initializing WemoBridgeHandler for UDN '{}'", configuration.get(UDN)); - super.initialize(); + updateStatus(ThingStatus.ONLINE); } else { logger.debug("Cannot initalize WemoBridgeHandler. UDN not set."); } diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoCoffeeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoCoffeeHandler.java index 343cb72bf66..95d3ce4a753 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoCoffeeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoCoffeeHandler.java @@ -127,7 +127,7 @@ public void initialize() { logger.debug("Initializing WemoCoffeeHandler for UDN '{}'", configuration.get("udn")); onSubscription(); onUpdate(); - super.initialize(); + updateStatus(ThingStatus.ONLINE); } else { logger.debug("Cannot initalize WemoCoffeeHandler. UDN not set."); } @@ -277,7 +277,7 @@ private synchronized void onUpdate() { refreshInterval = ((BigDecimal) refreshConfig).intValue(); logger.debug("Setting WemoCoffeeHandler refreshInterval to '{}' seconds", refreshInterval); } - refreshJob = scheduler.scheduleAtFixedRate(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); } } @@ -337,12 +337,12 @@ protected void updateWemoState() { NodeList deviceIndex = element.getElementsByTagName("name"); Element line = (Element) deviceIndex.item(0); String attributeName = getCharacterDataFromElement(line); - logger.trace("attributeName: " + attributeName); + logger.trace("attributeName: {}", attributeName); NodeList deviceID = element.getElementsByTagName("value"); line = (Element) deviceID.item(0); String attributeValue = getCharacterDataFromElement(line); - logger.trace("attributeValue: " + attributeValue); + logger.trace("attributeValue: {}", attributeValue); switch (attributeName) { case "Mode": diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoHandler.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoHandler.java index 8672571441d..b3dfd3026aa 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoHandler.java @@ -119,7 +119,7 @@ public void initialize() { logger.debug("Initializing WemoHandler for UDN '{}'", configuration.get("udn")); onSubscription(); onUpdate(); - super.initialize(); + updateStatus(ThingStatus.ONLINE); } else { logger.debug("Cannot initalize WemoHandler. UDN not set."); } @@ -409,7 +409,7 @@ private synchronized void onUpdate() { if (refreshConfig != null) { refreshInterval = ((BigDecimal) refreshConfig).intValue(); } - refreshJob = scheduler.scheduleAtFixedRate(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoLightHandler.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoLightHandler.java index 25a195d5701..5b75d1b2263 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoLightHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoLightHandler.java @@ -70,7 +70,7 @@ public class WemoLightHandler extends BaseThingHandler implements UpnpIOParticip * The default refresh interval in Seconds. */ private int DEFAULT_REFRESH_INTERVAL = 60; - + /** * The default refresh initial delay in Seconds. */ @@ -417,7 +417,8 @@ private synchronized void onUpdate() { refreshInterval = ((BigDecimal) refreshConfig).intValue(); } logger.trace("Start polling job for LightID '{}'", wemoLightID); - refreshJob = scheduler.scheduleAtFixedRate(refreshRunnable, DEFAULT_REFRESH_INITIAL_DELAY, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, DEFAULT_REFRESH_INITIAL_DELAY, + refreshInterval, TimeUnit.SECONDS); } } diff --git a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoMakerHandler.java b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoMakerHandler.java index 740a288ca93..58480711e17 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoMakerHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.wemo/src/main/java/org/eclipse/smarthome/binding/wemo/handler/WemoMakerHandler.java @@ -105,7 +105,7 @@ public void initialize() { if (configuration.get("udn") != null) { logger.debug("Initializing WemoMakerHandler for UDN '{}'", configuration.get("udn")); onUpdate(); - super.initialize(); + updateStatus(ThingStatus.ONLINE); } else { logger.debug("Cannot initalize WemoMakerHandler. UDN not set."); } @@ -203,7 +203,7 @@ private synchronized void onUpdate() { if (refreshConfig != null) { refreshInterval = ((BigDecimal) refreshConfig).intValue(); } - refreshJob = scheduler.scheduleAtFixedRate(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 0, refreshInterval, TimeUnit.SECONDS); } } } @@ -259,12 +259,12 @@ protected void updateWemoState() { NodeList deviceIndex = element.getElementsByTagName("name"); Element line = (Element) deviceIndex.item(0); String attributeName = getCharacterDataFromElement(line); - logger.trace("attributeName: " + attributeName); + logger.trace("attributeName: {}", attributeName); NodeList deviceID = element.getElementsByTagName("value"); line = (Element) deviceID.item(0); String attributeValue = getCharacterDataFromElement(line); - logger.trace("attributeValue: " + attributeValue); + logger.trace("attributeValue: {}", attributeValue); switch (attributeName) { case "Switch": diff --git a/extensions/binding/org.eclipse.smarthome.binding.yahooweather/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.yahooweather/META-INF/MANIFEST.MF index ec0e80118f5..f77405da1e1 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.yahooweather/META-INF/MANIFEST.MF +++ b/extensions/binding/org.eclipse.smarthome.binding.yahooweather/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: . Import-Package: com.google.common.collect, org.apache.commons.lang, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.binding.yahooweather, org.eclipse.smarthome.binding.yahooweather.handler, org.eclipse.smarthome.config.core, diff --git a/extensions/binding/org.eclipse.smarthome.binding.yahooweather/src/main/java/org/eclipse/smarthome/binding/yahooweather/handler/YahooWeatherHandler.java b/extensions/binding/org.eclipse.smarthome.binding.yahooweather/src/main/java/org/eclipse/smarthome/binding/yahooweather/handler/YahooWeatherHandler.java index 8d02796190f..c012f687bc5 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.yahooweather/src/main/java/org/eclipse/smarthome/binding/yahooweather/handler/YahooWeatherHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.yahooweather/src/main/java/org/eclipse/smarthome/binding/yahooweather/handler/YahooWeatherHandler.java @@ -41,7 +41,7 @@ * @author Stefan Bußweiler - Integrate new thing status handling * @author Thomas Höfer - Added config status provider * @author Christoph Weitkamp - Changed use of caching utils to ESH ExpiringCacheMap - * + * */ public class YahooWeatherHandler extends ConfigStatusThingHandler { @@ -104,7 +104,7 @@ public void dispose() { } private void startAutomaticRefresh() { - refreshJob = scheduler.scheduleAtFixedRate(() -> { + refreshJob = scheduler.scheduleWithFixedDelay(() -> { try { boolean success = updateWeatherData(); if (success) { diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java index 0ddf6368462..b79f16d02d6 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace.automation/src/main/java/org/eclipse/smarthome/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java @@ -46,11 +46,6 @@ public Collection getTemplates(Locale locale) { return getAll(); } - @Override - protected String getKey(RuleTemplate element) { - return element.getUID(); - } - @Override protected String getStorageName() { return "org.eclipse.smarthome.extensionservice.marketplace.RuleTemplates"; diff --git a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF index 9d7e526315c..65a187304c8 100644 --- a/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF +++ b/extensions/extensionservice/org.eclipse.smarthome.extensionservice.marketplace/META-INF/MANIFEST.MF @@ -16,6 +16,7 @@ Import-Package: com.thoughtworks.xstream, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.extension, org.eclipse.smarthome.core.storage, + org.eclipse.smarthome.extensionservice.marketplace, org.osgi.framework, org.slf4j Bundle-ActivationPolicy: lazy diff --git a/extensions/io/org.eclipse.smarthome.io.javasound/src/main/java/org/eclipse/smarthome/io/javasound/internal/AudioPlayer.java b/extensions/io/org.eclipse.smarthome.io.javasound/src/main/java/org/eclipse/smarthome/io/javasound/internal/AudioPlayer.java index cecf9e0bdd3..69f3caf90e8 100644 --- a/extensions/io/org.eclipse.smarthome.io.javasound/src/main/java/org/eclipse/smarthome/io/javasound/internal/AudioPlayer.java +++ b/extensions/io/org.eclipse.smarthome.io.javasound/src/main/java/org/eclipse/smarthome/io/javasound/internal/AudioPlayer.java @@ -70,7 +70,7 @@ public void run() { mixer = AudioSystem.getMixer(mixerInfo[cnt]); Line.Info[] lineInfos = mixer.getSourceLineInfo(); for (Info lineInfo : lineInfos) { - logger.info(lineInfo.toString()); + logger.info("{}", lineInfo); } } return; diff --git a/extensions/pom.xml b/extensions/pom.xml index 9a40a9281ce..761675b8630 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -39,7 +39,28 @@ + + org.apache.felix + maven-scr-plugin + + + generate-scr-scrdescriptor + + scr + + + + + + + org.apache.felix + org.apache.felix.scr.ds-annotations + ${ds-annotations.version} + true + + + diff --git a/extensions/transform/org.eclipse.smarthome.transform.exec/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.exec/META-INF/MANIFEST.MF index 3c81d2e9bd7..63f42f38d44 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.exec/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.exec/META-INF/MANIFEST.MF @@ -9,6 +9,6 @@ Import-Package: org.eclipse.smarthome.core.transform, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.exec Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/execservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.javascript/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.javascript/META-INF/MANIFEST.MF index 1a1a15e77e1..0b2a779e6be 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.javascript/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.javascript/META-INF/MANIFEST.MF @@ -11,6 +11,6 @@ Import-Package: javax.script, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.javascript Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/javascriptservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.jsonpath/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.jsonpath/META-INF/MANIFEST.MF index 5dfda57bbd5..c9bb14c58bd 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.jsonpath/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.jsonpath/META-INF/MANIFEST.MF @@ -14,5 +14,5 @@ Import-Package: com.jayway.jsonpath, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.jsonpath Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/jsonpathservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.map/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.map/META-INF/MANIFEST.MF index 3636d50a16d..373a285576c 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.map/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.map/META-INF/MANIFEST.MF @@ -10,6 +10,6 @@ Import-Package: org.eclipse.smarthome.core.i18n, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.map Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/mapservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.regex/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.regex/META-INF/MANIFEST.MF index 1ddd8bf1ffb..96796d6d0ed 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.regex/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.regex/META-INF/MANIFEST.MF @@ -9,6 +9,6 @@ Import-Package: org.eclipse.smarthome.config.core, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.regex Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/regexservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.scale/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.scale/META-INF/MANIFEST.MF index 9687e66e667..40801363826 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.scale/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.scale/META-INF/MANIFEST.MF @@ -10,6 +10,6 @@ Import-Package: org.eclipse.smarthome.core.i18n, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.scale Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/scaleservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.xpath/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.xpath/META-INF/MANIFEST.MF index 7c31836ff7c..25b46764bea 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.xpath/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.xpath/META-INF/MANIFEST.MF @@ -12,6 +12,6 @@ Import-Package: javax.xml.parsers, org.xml.sax Bundle-SymbolicName: org.eclipse.smarthome.transform.xpath Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/xpathservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.xslt/META-INF/MANIFEST.MF b/extensions/transform/org.eclipse.smarthome.transform.xslt/META-INF/MANIFEST.MF index bd592881edb..15a1c1c7f76 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.xslt/META-INF/MANIFEST.MF +++ b/extensions/transform/org.eclipse.smarthome.transform.xslt/META-INF/MANIFEST.MF @@ -11,6 +11,6 @@ Import-Package: javax.xml.transform, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.transform.xslt Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/xsltservice.xml +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: . diff --git a/extensions/transform/org.eclipse.smarthome.transform.xslt/src/main/java/org/eclipse/smarthome/transform/xslt/internal/XsltTransformationService.java b/extensions/transform/org.eclipse.smarthome.transform.xslt/src/main/java/org/eclipse/smarthome/transform/xslt/internal/XsltTransformationService.java index 15ae51b937d..60ca863579a 100644 --- a/extensions/transform/org.eclipse.smarthome.transform.xslt/src/main/java/org/eclipse/smarthome/transform/xslt/internal/XsltTransformationService.java +++ b/extensions/transform/org.eclipse.smarthome.transform.xslt/src/main/java/org/eclipse/smarthome/transform/xslt/internal/XsltTransformationService.java @@ -35,13 +35,13 @@ public class XsltTransformationService implements TransformationService { /** * Transforms the input source by XSLT. - * + * * The method expects the transformation rule to be read from a file which * is stored under the 'configurations/transform' folder. To organize the * various transformations one should use subfolders. * * @param filename - * the name of the file which contains the XSLT transformation rule. + * the name of the file which contains the XSLT transformation rule. * The name may contain subfoldernames as well * @param source * the input to transform @@ -62,7 +62,7 @@ public String transform(String filename, String source) throws TransformationExc } catch (Exception e) { String message = "opening file '" + filename + "' throws exception"; - logger.error(message, e); + logger.error("{}", message, e); throw new TransformationException(message, e); } diff --git a/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/META-INF/MANIFEST.MF b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/META-INF/MANIFEST.MF index 5b458fd0415..a6d28b414fb 100644 --- a/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/META-INF/MANIFEST.MF +++ b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/META-INF/MANIFEST.MF @@ -9,4 +9,4 @@ Import-Package: org.eclipse.smarthome.core.i18n, org.eclipse.smarthome.ui.icon, org.osgi.framework, org.slf4j -Service-Component: OSGI-INF/classiciconprovider.xml +Service-Component: OSGI-INF/*.xml diff --git a/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/about.html b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/about.html new file mode 100644 index 00000000000..2ba76b399bd --- /dev/null +++ b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/about.html @@ -0,0 +1,28 @@ + + + + +About + + +

    About This Content

    + +

    July 24, 2017

    +

    License

    + +

    The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

    + +

    If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

    + + + \ No newline at end of file diff --git a/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/build.properties b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/build.properties index 5f906b3e939..56ced993ff7 100644 --- a/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/build.properties +++ b/extensions/ui/iconset/org.eclipse.smarthome.ui.iconset.classic/build.properties @@ -3,5 +3,6 @@ bin.includes = META-INF/,\ .,\ OSGI-INF/,\ icons/,\ - ESH-INF/ + ESH-INF/,\ + about.html source.. = src/main/java/ diff --git a/extensions/ui/org.eclipse.smarthome.ui.basic/src/main/java/org/eclipse/smarthome/ui/basic/internal/WebAppActivator.java b/extensions/ui/org.eclipse.smarthome.ui.basic/src/main/java/org/eclipse/smarthome/ui/basic/internal/WebAppActivator.java index 8afa64f9327..1c5c76590e9 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.basic/src/main/java/org/eclipse/smarthome/ui/basic/internal/WebAppActivator.java +++ b/extensions/ui/org.eclipse.smarthome.ui.basic/src/main/java/org/eclipse/smarthome/ui/basic/internal/WebAppActivator.java @@ -12,6 +12,8 @@ /** * Extension of the default OSGi bundle activator + * + * @author Vlad Ivanov - Initial contribution */ public final class WebAppActivator implements BundleActivator { diff --git a/extensions/ui/org.eclipse.smarthome.ui.basic/web-src/smarthome.js b/extensions/ui/org.eclipse.smarthome.ui.basic/web-src/smarthome.js index 5d1477280ad..d17dcca573e 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.basic/web-src/smarthome.js +++ b/extensions/ui/org.eclipse.smarthome.ui.basic/web-src/smarthome.js @@ -1742,22 +1742,29 @@ widget: widget, visibility: data.visibility }); - } else { - widget.setValue(smarthome.UI.escapeHtml(value), data.item.state); - if (data.label !== undefined) { - widget.setLabel(data.label); - } - if (data.labelcolor !== undefined) { - widget.setLabelColor(data.labelcolor); - } else { - widget.setLabelColor(""); - } - if (data.valuecolor !== undefined) { - widget.setValueColor(data.valuecolor); - } else { - widget.setValueColor(""); - } } + + widget.setValue(smarthome.UI.escapeHtml(value), data.item.state); + + [{ + apply: widget.setLabel, + data: data.label, + fallback: null + }, { + apply: widget.setLabelColor, + data: data.labelcolor, + fallback: "" + }, { + apply: widget.setValueColor, + data: data.valuecolor, + fallback: "" + }].forEach(function(e) { + if (e.data !== undefined) { + e.apply(e.data); + } else if (e.fallback !== null) { + e.apply(e.fallback); + } + }); } }); diff --git a/extensions/ui/org.eclipse.smarthome.ui.basic/web/smarthome.js b/extensions/ui/org.eclipse.smarthome.ui.basic/web/smarthome.js index 0d8c2727dee..925f1f03cbe 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.basic/web/smarthome.js +++ b/extensions/ui/org.eclipse.smarthome.ui.basic/web/smarthome.js @@ -1 +1 @@ -!function(a){"use strict";function b(a){var b=document.createElement("div");return b.innerHTML=a,b.childNodes}function c(a,b){b instanceof NodeList?[].slice.call(b).forEach(function(b){a.appendChild(b)}):a.appendChild(b)}function d(a){var b=a,c=void 0!==b.type?b.type:"GET",d=void 0!==b.data?b.data:"",e=void 0!==b.headers?b.headers:{},f=new XMLHttpRequest;f.open(c,b.url,!0);for(var g in e)f.setRequestHeader(g,e[g]);return f.onload=function(){if(f.status<200||f.status>400)return void("function"==typeof b.error&&b.error(f));"function"==typeof b.callback&&b.callback(f)},f.onerror=function(){"function"==typeof b.error&&b.error(f)},f.send(d),f}function e(a,b){return document.getElementById(a).innerHTML.replace(/\{([\w]+)\}/,function(a,c){return void 0!==b[c]?b[c]:""})}function f(){var a=this;a.level=0,a.push=function(b){a.level++,history.pushState({page:b},document.title.textContent)},a.replace=function(a,b){history.replaceState({page:a},document.title.textContent,b)},window.addEventListener("popstate",function(b){a.level--,D.UI.navigate(b.state.page,!1)},!1)}function g(d){function f(a){a=a||window.event,27===a.keyCode&&i.hide()}function g(){document.addEventListener("keydown",f)}function h(){void 0!==i.onHide&&i.onHide(),document.removeEventListener("keydown",f)}var i=this;i.templateId="template-modal",i.text=e(i.templateId,{content:d}),i.show=function(){c(document.body,b(i.text)),i.bg=document.querySelector(a.modal),i.container=i.bg.querySelector(a.modalContainer),D.UI.currentModal=i,i.bg.addEventListener("click",function(){i.hide()}),i.container.addEventListener("click",function(a){a.stopPropagation()}),g()},i.hide=function(){document.body.querySelector(a.modal).remove(),delete D.UI.currentModal,h()}}function h(a,b){var c,d=this,e=!1;d.lock=!1,d.call=function(){c=arguments,d.lock||(e=!1,a.apply(null,c),d.lock=!0,setTimeout(function(){e||a.apply(null,c),d.lock=!1},b))},d.finish=function(){e=!0}}function i(a,b){function c(a){a.widget.setVisible(a.visibility)}var d=this;d.queue=[],d.timeout=null,d.processEvents=function(){for(d.timeout=null;0!==d.queue.length;)c(d.queue[0]),d.queue=d.queue.slice(1)},d.push=function(c){d.queue.length>b||(d.queue.push(c),null===d.timeout?d.timeout=setTimeout(d.processEvents,a):(clearTimeout(d.timeout),d.timeout=setTimeout(d.processEvents,a)))}}function j(b){var c=this,d=!1;c.parentNode=b,c.formRow=b.parentNode,c.item=c.parentNode.getAttribute(a.itemAttribute),c.id=c.parentNode.getAttribute(a.idAttribute),c.icon=c.parentNode.parentNode.querySelector(a.formIcon),c.visible=!c.formRow.classList.contains(a.formRowHidden),c.label=c.parentNode.parentNode.querySelector(a.formLabel),null!==c.icon&&(c.iconName=c.icon.getAttribute(a.iconAttribute)),c.reloadIcon=function(a){null!==c.icon&&c.icon.setAttribute("src","/icon/"+c.iconName+"?state="+encodeURIComponent(a)+"&format="+D.UI.iconType)},c.setVisible=function(b){b?c.formRow.classList.remove(a.formRowHidden):c.formRow.classList.add(a.formRowHidden),c.visible=b},c.setValue=function(a,b){c.reloadIcon(b),d?d=!1:c.setValuePrivate(a,b)},c.setValuePrivate=function(){},c.setLabel=function(){},c.suppressUpdate=function(){d=!0},c.setLabelColor=function(a){null!==c.label&&(c.label.style.color=a)},c.setValueColor=function(a){c.parentNode.style.color=a}}function k(b){var c=this;c.parentNode=b,c.id=c.parentNode.getAttribute(a.idAttribute),c.visible=!c.parentNode.classList.contains(a.formHidden),c.title=c.parentNode.querySelector(a.formTitle),c.setVisible=function(b){b?c.parentNode.classList.remove(a.formHidden):c.parentNode.classList.add(a.formHidden),c.visible=b},c.setLabel=function(a){c.title.innerHTML=a},c.setValue=function(){},c.suppressUpdate=function(){}}function l(a,b){b&&j.call(this,a);var c=this;if(c.image=a.querySelector("img"),c.updateInterval=parseInt(a.getAttribute("data-update-interval"),10),c.url=a.getAttribute("data-proxied-url"),c.validUrl="true"===a.getAttribute("data-valid-url"),c.setValuePrivate=function(a,b){b.startsWith("data:")?c.image.setAttribute("src",b):"UNDEF"!==b||c.validUrl?c.image.setAttribute("src",c.url+"&t="+Date.now()):c.image.setAttribute("src","images/none.png")},0!==c.updateInterval){c.updateInterval<100&&(c.updateInterval=100);var d=setInterval(function(){if(0===c.image.clientWidth)return void clearInterval(d);c.image.setAttribute("src",c.url+"&t="+Date.now())},c.updateInterval)}}function m(a){j.call(this,a);var b=this;b.hasValue="true"===b.parentNode.getAttribute("data-has-value"),b.setValuePrivate=function(c){b.hasValue&&(a.innerHTML=c)}}function n(b){j.call(this,b);var c=this;c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.value=c.parentNode.parentNode.querySelector(a.formValue),c.count=1*c.parentNode.getAttribute("data-count"),c.suppressUpdateButtons=!1,c.reset=function(){c.buttons.forEach(function(b){b.classList.remove(a.buttonActiveClass)})},c.onclick=function(){var b=this.getAttribute("data-value")+"";1!==c.count&&(c.reset(),this.classList.add(a.buttonActiveClass)),c.parentNode.dispatchEvent(C("control-change",{item:c.item,value:b})),c.suppressUpdateButtons=!0},c.valueMap={},c.buttons=[].slice.call(c.parentNode.querySelectorAll(a.controlButton)),c.setValuePrivate=function(b,d){if(c.hasValue&&(c.value.innerHTML=b),1!==c.count){if(c.suppressUpdateButtons)return void(c.suppressUpdateButtons=!1);c.reset(),void 0!==c.valueMap&&void 0!==c.valueMap[d]&&c.valueMap[d].classList.add(a.buttonActiveClass)}},c.setValueColor=function(a){c.value.style.color=a},c.buttons.forEach.call(c.buttons,function(a){var b=a.getAttribute("data-value")+"";c.valueMap[b]=a,a.addEventListener("click",c.onclick)})}function o(b){function c(a){if(a.stopPropagation(),"input"===a.target.tagName.toLowerCase()){var b=a.target.getAttribute("value");d.parentNode.dispatchEvent(C("control-change",{item:d.item,value:b})),setTimeout(function(){d.modal.hide()},300)}}j.call(this,b);var d=this,e=b.getAttribute("data-value-map");d.value=null,d.valueNode=b.parentNode.querySelector(a.formValue),d.valueMap=null!==e?JSON.parse(e):{},d.showModal=function(){var b=d.parentNode.querySelector(a.selectionRows).innerHTML;d.modal=new g(b),d.modal.show();var e=[].slice.call(d.modal.container.querySelectorAll(a.formRadio));if(null!==d.value){[].slice.call(d.modal.container.querySelectorAll("input[type=radio]")).forEach(function(a){a.value===d.value?a.checked=!0:a.checked=!1})}e.forEach(function(a){componentHandler.upgradeElement(a,"MaterialRadio"),a.addEventListener("click",c)})},d.setValuePrivate=function(a,b){d.value=""+b,void 0!==d.valueMap[b]?d.valueNode.innerHTML=D.UI.escapeHtml(d.valueMap[b]):d.valueNode.innerHTML=""},d.setValueColor=function(a){d.valueNode.style.color=a},d.parentNode.parentNode.addEventListener("click",d.showModal)}function p(b){function c(a){i.parentNode.dispatchEvent(C("control-change",{item:i.item,value:a}))}function d(a,b){g=!1,l=!0,h=setTimeout(function(){g=!0,c(a)},k),b.stopPropagation(),b.preventDefault()}function e(a,b){clearTimeout(h),l&&(l=!1,c(g?"STOP":a),b.stopPropagation(),b.preventDefault())}function f(a){c("STOP"),a.stopPropagation(),a.preventDefault()}j.call(this,b);var g,h,i=this,k=300,l=!1;i.buttonUp=i.parentNode.querySelector(a.rollerblind.up),i.buttonDown=i.parentNode.querySelector(a.rollerblind.down),i.buttonStop=i.parentNode.querySelector(a.rollerblind.stop),i.hasValue="true"===i.parentNode.getAttribute("data-has-value"),i.valueNode=i.parentNode.parentNode.querySelector(a.formValue),i.setValuePrivate=function(a){i.hasValue&&("DOWN"===a?a="100":"UP"===a&&(a="0"),i.valueNode.innerHTML=a)};var m=e.bind(null,"UP"),n=d.bind(null,"UP"),o=e.bind(null,"DOWN"),p=d.bind(null,"DOWN");i.buttonUp.addEventListener("touchstart",n),i.buttonUp.addEventListener("mousedown",n),i.buttonUp.addEventListener("touchend",m),i.buttonUp.addEventListener("mouseup",m),i.buttonUp.addEventListener("mouseleave",m),i.buttonDown.addEventListener("touchstart",p),i.buttonDown.addEventListener("mousedown",p),i.buttonDown.addEventListener("touchend",o),i.buttonDown.addEventListener("mouseup",o),i.buttonDown.addEventListener("mouseleave",o),i.buttonStop.addEventListener("mousedown",f),i.buttonStop.addEventListener("touchstart",f)}function q(b){function c(a,b){var c=d.value+(!0===a?d.step:-d.step);c=c>d.max?d.max:c,c=cc*c){var h=1-Math.abs(c/Math.sqrt(g));b.x-=d*h,b.y-=e*h}p.handle.style.left=b.x/p.image.clientWidth*100+"%",p.handle.style.top=b.y/p.image.clientWidth*100+"%";var i=d>=0?(2*Math.PI-Math.atan(e/d)+Math.PI/2)/(2*Math.PI):(2*Math.PI-Math.atan(e/d)-Math.PI/2)/(2*Math.PI),j={h:isNaN(i)?0:i,s:Math.sqrt(g)/c,v:1},k=f(j);p.hsvValue={h:j.h,s:j.s,v:p.slider.value/100},k.l=k.l<.5?.5:k.l,p.background.style.background="hsl("+360*k.h+", 100%, "+Math.round(100*k.l)+"%)"}function i(a){var b=e(a);p.slider.value=100*b.v;var c=50+Math.round(b.s*Math.cos(2*Math.PI*b.h)*50),d=50+Math.round(b.s*Math.sin(2*Math.PI*b.h)*50);p.handle.style.top=c+"%",p.handle.style.left=d+"%",b.v=1;var f=r.hsv2rgb(b);p.background.style.background="rgb("+Math.round(f.r)+","+Math.round(f.g)+","+Math.round(f.b)+")"}function j(){null!==p.interval&&(clearInterval(p.interval),p.interval=null),p.isBeingChanged=!1,window.removeEventListener("mouseup",j)}function k(a){p.interval=setInterval(function(){d(p.hsvValue)},300),window.addEventListener("mouseup",j),g(a),d(p.hsvValue),p.isBeingChanged=!0,a.stopPropagation()}function l(a){(void 0!==a.touches||1&a.buttons)&&(g(a),a.stopPropagation(),a.preventDefault())}function m(a){null!==p.interval&&(clearInterval(p.interval),p.interval=null),window.removeEventListener("mouseup",j),p.isBeingChanged=!1,a.stopPropagation()}function n(){p.hsvValue.v=p.slider.value/100,p.debounceProxy.call()}function o(){p.hsvValue.v=p.slider.value/100,p.debounceProxy.call(),p.debounceProxy.finish()}var p=this;p.container=b,p.value=c,p.hsvValue=e(c),p.interval=null,p.isBeingChanged=!1,p.colorpicker=p.container.querySelector(a.colorpicker.colorpicker),p.image=p.container.querySelector(a.colorpicker.image),p.background=p.container.querySelector(a.colorpicker.background),p.handle=p.container.querySelector(a.colorpicker.handle),p.slider=p.container.querySelector(a.colorpicker.slider),p.button=p.container.querySelector(a.controlButton),componentHandler.upgradeElement(p.button,"MaterialButton"),componentHandler.upgradeElement(p.button,"MaterialRipple"),p.debounceProxy=new h(function(){d(p.hsvValue)},200),p.updateColor=function(a){p.isBeingChanged||i(a)},p.slider.addEventListener("change",n),p.slider.addEventListener("touchend",o),p.slider.addEventListener("mouseup",o),p.image.addEventListener("mousedown",l),p.image.addEventListener("mousemove",l),p.image.addEventListener("touchmove",l),p.image.addEventListener("touchstart",l),p.image.addEventListener("touchend",m),p.image.addEventListener("mouseup",m),p.image.addEventListener("mousedown",k),p.image.addEventListener("touchstart",k),i(c)}function s(b){function c(a){return{r:parseInt(a.substr(1,2),16),g:parseInt(a.substr(3,2),16),b:parseInt(a.substr(5,2),16)}}function d(a){l.parentNode.dispatchEvent(C("control-change",{item:l.item,value:a}))}function f(a){l.pressed=!0,l.longPress=!1,k=setInterval(function(){l.longPress=!0,d(a)},m)}function h(a){l.pressed&&(l.longPress||d(a),l.pressed=!1,l.longPress=!1,clearInterval(k))}function i(){l.modal=new g(e("template-colorpicker")),l.modal.show(),l.modal.container.classList.add(a.colorpicker.modalClass),l.modal.onHide=function(){l.modalControl=null,l.modal=null},l.modalControl=new r(l.modal.container,l.value,function(a){l.value=r.hsv2rgb(a),d(Math.round(360*a.h%360)+","+Math.round(100*a.s%100)+","+Math.round(100*a.v))}),l.modal.container.querySelector(a.colorpicker.button).addEventListener("click",function(){l.modal.hide()})}j.call(this,b);var k,l=this,m=300;l.value=c(l.parentNode.getAttribute("data-value")),l.modalControl=null,l.buttonUp=l.parentNode.querySelector(a.colorpicker.up),l.buttonDown=l.parentNode.querySelector(a.colorpicker.down),l.buttonPick=l.parentNode.querySelector(a.colorpicker.pick),l.longPress=!1,l.pressed=!1,l.setValue=function(a){var b=a.split(","),c={h:b[0]/360,s:b[1]/100,v:b[2]/100};l.value=r.hsv2rgb(c),null!==l.modalControl&&l.modalControl.updateColor(l.value)};var n=f.bind(null,"INCREASE"),o=f.bind(null,"DECREASE"),p=h.bind(null,"ON"),q=h.bind(null,"OFF");l.buttonUp.addEventListener("touchstart",n),l.buttonUp.addEventListener("mousedown",n),l.buttonUp.addEventListener("mouseleave",p),l.buttonUp.addEventListener("touchend",p),l.buttonUp.addEventListener("mouseup",p),l.buttonDown.addEventListener("touchstart",o),l.buttonDown.addEventListener("mousedown",o),l.buttonDown.addEventListener("touchend",q),l.buttonDown.addEventListener("mouseup",q),l.buttonDown.addEventListener("mouseleave",q),l.buttonPick.addEventListener("click",i)}function t(b){j.call(this,b);var c=this;c.input=c.parentNode.querySelector("input[type=checkbox]"),c.input.addEventListener("change",function(){c.parentNode.dispatchEvent(C("control-change",{item:c.item,value:c.input.checked?"ON":"OFF"}))}),c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.valueNode=c.parentNode.parentNode.querySelector(a.formValue),c.setValuePrivate=function(a,b){var d="ON"===b;c.input.checked!==d&&(c.input.checked=d,d?c.parentNode.MaterialSwitch.on():c.parentNode.MaterialSwitch.off()),c.hasValue&&(c.valueNode.innerHTML=a)},c.setValueColor=function(a){c.valueNode.style.color=a}}function u(b){function c(){f.parentNode.dispatchEvent(C("control-change",{item:f.item,value:f.input.value}))}function d(){null!==g&&clearTimeout(g),f.locked=!0,D.changeListener.pause()}function e(){f.debounceProxy.call(),setTimeout(function(){D.changeListener.resume()},5),g=setTimeout(function(){f.locked=!1},300),f.debounceProxy.finish()}j.call(this,b);var f=this;f.input=f.parentNode.querySelector("input[type=range]"),f.hasValue="true"===f.parentNode.getAttribute("data-has-value"),f.valueNode=f.parentNode.parentNode.querySelector(a.formValue),f.locked=!1,function(){var a=parseInt(f.input.getAttribute("data-state"),10);isNaN(a)?f.input.value=0:f.input.value=a,f.input.MaterialSlider&&f.input.MaterialSlider.change()}(),f.debounceProxy=new h(function(){c()},200),f.input.addEventListener("change",function(){f.debounceProxy.call()}),f.setValuePrivate=function(a,b){if(f.hasValue&&(f.valueNode.innerHTML=a),f.locked)return void f.reloadIcon(b);f.input.value=b,f.input.MaterialSlider.change()},f.setValueColor=function(a){f.valueNode.style.color=a};var g=null;f.input.addEventListener("touchstart",d),f.input.addEventListener("mousedown",d),f.input.addEventListener("touchend",e),f.input.addEventListener("mouseup",e)}function v(b){j.call(this,b);var c=this;c.target=b.getAttribute("data-target"),c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.container=b.parentNode.querySelector(a.formValue),c.setValuePrivate=function(a){c.hasValue&&(c.container.innerHTML=a)},c.setValueColor=function(a){c.container.style.color=a},b.parentNode.addEventListener("click",function(){D.UI.navigate(c.target,!0)})}function w(a){d({type:"POST",url:"/rest/items/"+a.detail.item,data:a.detail.value,headers:{"Content-Type":"text/plain"}})}function x(b){function c(a){var b=a.documentElement,c=[];if("page"===b.tagName){[].forEach.call(b.childNodes,function(a){a instanceof Text||c.push(a)}),g.setTitle(c[0].textContent);for(var d=document.querySelector(".page-content");d.firstChild;)d.removeChild(d.firstChild);d.insertAdjacentHTML("beforeend",c[1].textContent)}}var e={Loading:1,Idle:2},g=this,h=e.Idle,i=new f;g.page=document.body.getAttribute("data-page-id"),g.sitemap=document.body.getAttribute("data-sitemap"),g.destination=null,g.root=b,g.loading=g.root.querySelector(a.uiLoadingBar),g.layoutTitle=document.querySelector(a.layoutTitle),g.iconType=document.body.getAttribute(a.iconTypeAttribute),g.notification=document.querySelector(a.notify),g.escapeHtml=function(a){for(var b=a,c=[[/&/g,"&"],[//g,">"]],d=0;d0?history.back():location.href=location.origin+"/basicui/app?sitemap="+D.UI.sitemap})}()}function y(){this.paused=!1,this.pause=function(){this.paused=!0},this.resume=function(){this.paused=!1},this.extractValueFromLabel=function(a){var b=null;if("string"==typeof a&&-1!==a.indexOf("[")&&-1!==a.indexOf("]")){var c=a.indexOf("[")+1;b=a.substr(c,a.lastIndexOf("]")-c)}return b},this.getTitleFromLabel=function(a){var b=this.extractValueFromLabel(a),c=null;return null!==b&&(c=a.substr(0,a.indexOf("["))+b),c}}function z(a){y.call(this);var b=this;b.navigate=function(){},b.source=new EventSource(a),b.source.addEventListener("event",function(a){if(!b.paused){var c,d,e=JSON.parse(a.data);if("SITEMAP_CHANGED"===e.TYPE){var f=window.location.href,g=f.split("?");return g.length>1?window.location.href=g[0]+"?sitemap="+e.sitemapName:window.location.reload(!0),void b.pause()}if(e.widgetId in D.dataModel||e.widgetId===D.UI.page)if(c=b.extractValueFromLabel(e.label),null===c&&(c=e.item.state),d=b.getTitleFromLabel(e.label),e.widgetId===D.UI.page&&null!==d)D.UI.setTitle(D.UI.escapeHtml(d));else if(void 0!==D.dataModel[e.widgetId]){var h=D.dataModel[e.widgetId];h.visible!==e.visibility?D.UI.layoutChangeProxy.push({widget:h,visibility:e.visibility}):(h.setValue(D.UI.escapeHtml(c),e.item.state),void 0!==e.label&&h.setLabel(e.label),void 0!==e.labelcolor?h.setLabelColor(e.labelcolor):h.setLabelColor(""),void 0!==e.valuecolor?h.setValueColor(e.valuecolor):h.setValueColor(""))}}}),b.source.onerror=function(){b.source.close(),b.connectionError()}}function A(){function a(a){function b(a){a.forEach(function(a){if(void 0!==a.item){var b=a.item.name,d=a.item.state,e=a.label,f=c.extractValueFromLabel(a.label),g=a.labelcolor,h=a.valuecolor;null===f&&(f=d),void 0!==D.dataModelLegacy[b]&&D.dataModelLegacy[b].widgets.forEach(function(a){"NULL"!==d&&a.setValue(D.UI.escapeHtml(f),d),void 0!==e&&a.setLabel(e),void 0!==g?a.setLabelColor(g):a.setLabelColor(""),void 0!==h?a.setValueColor(h):a.setValueColor("")})}})}var d;try{a=JSON.parse(a)}catch(e){return}d=c.getTitleFromLabel(a.title),null!==d&&D.UI.setTitle(D.UI.escapeHtml(d)),a.leaf?b(a.widgets):a.widgets.forEach(function(a){b(a.widgets)})}function b(){var e=Math.random().toString(16).slice(2);c.request=d({url:"/rest/sitemaps/"+c.sitemap+"/"+c.page+"?_="+e,headers:{"X-Atmosphere-Transport":"long-polling"},callback:function(d){c.paused||a(d.responseText),setTimeout(function(){b()},1)},error:function(){setTimeout(function(){b()},1e3)}})}y.call(this);var c=this;c.sitemap=document.body.getAttribute("data-sitemap"),c.page=document.body.getAttribute("data-page-id"),c.navigate=function(a){c.request.abort(),c.page=a,b()},c.pause=function(){c.request.abort(),c.paused=!0},c.resume=function(){c.paused=!1,b()},b()}function B(){function b(a){E.eventSource?z.call(f,a):A.call(f)}function c(){f.startSubscriber(f.subscribeResponse),f.subscribeResponse=null}var f=this;f.subscribeRequestURL="/rest/sitemaps/events/subscribe",f.reconnectInterval=null,f.subscribeResponse=null,f.suppressErrorsState=!1,f.connectionRestored=function(a){clearInterval(f.reconnectInterval),f.navigate=c,f.subscribeResponse=a,D.UI.hideNotification(),D.UI.navigate(D.UI.page,!1)},f.connectionError=function(){if(!f.suppressErrorsState){var b=e(a.notifyTemplateOffline,{});D.UI.showNotification(b),f.reconnectInterval=setInterval(function(){d({url:f.subscribeRequestURL,type:"POST",callback:f.connectionRestored})},1e4)}},f.suppressErrors=function(){f.suppressErrorsState=!0},f.startSubscriber=function(a){var c,d,e,f,g,h;try{c=JSON.parse(a.responseText)}catch(i){return}if("CREATED"===c.status){try{d=c.context.headers.Location[0]}catch(i){return}e=d.split("/"),g=e[e.length-1],f=document.body.getAttribute("data-sitemap"),h=document.body.getAttribute("data-page-id"),D.subscriptionId=g,b(d+"?sitemap="+f+"&pageid="+h)}},d({url:f.subscribeRequestURL,type:"POST",callback:f.startSubscriber})}var C,D={dataModel:{}},E={eventLayerXY:function(){var a;return a=void 0===document.createEvent?new MouseEvent(null):document.createEvent("MouseEvent"),void 0!==a.layerX}(),pointerEvents:void 0!==document.createElement("div").style.pointerEvents,customEvent:function(){var a=!0;try{new CustomEvent("event",{})}catch(b){a=!1}return a}(),elementRemove:void 0!==Element.prototype.remove,flexbox:void 0!==document.createElement("div").style.flexBasis,flexboxLegacy:function(){var a=document.createElement("div");return void 0!==a.style.boxDirection||void 0!==a.style.webkitBoxDirection}(),eventSource:"EventSource"in window};!function(){C=E.customEvent?function(a,b){return new CustomEvent(a,{detail:b})}:function(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!0,!1,b),c},E.elementRemove||(Element.prototype.remove=function(){this.parentNode.removeChild(this)}),!E.flexbox&&E.flexboxLegacy&&document.documentElement.classList.add("flexbox-legacy")}(),r.hsv2rgb=function(a){var b,c,d,e=a.h,f=a.s,g=a.v,h=Math.floor(6*e),i=6*e-h,j=g*(1-f),k=g*(1-i*f),l=g*(1-(1-i)*f);switch(h%6){case 0:b=g,c=l,d=j;break;case 1:b=k,c=g,d=j;break;case 2:b=j,c=g,d=l;break;case 3:b=j,c=k,d=g;break;case 4:b=l,c=j,d=g;break;case 5:b=g,c=j,d=k}return{r:255*b,g:255*c,b:255*d}},document.addEventListener("DOMContentLoaded",function(){D.UI=new x(document),D.UI.layoutChangeProxy=new i(100,50),D.UI.initControls(),D.changeListener=new B,window.addEventListener("beforeunload",function(){D.changeListener.suppressErrors()})})}({itemAttribute:"data-item",idAttribute:"data-widget-id",iconAttribute:"data-icon",iconTypeAttribute:"data-icon-type",controlButton:"button",buttonActiveClass:"mdl-button--accent",modal:".mdl-modal",modalContainer:".mdl-modal__content",selectionRows:".mdl-form__selection-rows",form:".mdl-form",formTitle:".mdl-form__title",formHidden:"mdl-form--hidden",formControls:".mdl-form__control",formRowHidden:"mdl-form__row--hidden",formValue:".mdl-form__value",formRadio:".mdl-radio",formRadioControl:".mdl-radio__button",formIcon:".mdl-form__icon img",formLabel:".mdl-form__label",uiLoadingBar:".ui__loading",layoutTitle:".mdl-layout-title",layoutHeader:".mdl-layout__header",backButton:".navigation__button-back",rollerblind:{up:".mdl-form__rollerblind--up",down:".mdl-form__rollerblind--down",stop:".mdl-form__rollerblind--stop"},setpoint:{up:".mdl-form__setpoint--up",down:".mdl-form__setpoint--down"},colorpicker:{up:".mdl-form__colorpicker--up",down:".mdl-form__colorpicker--down",pick:".mdl-form__colorpicker--pick",modalClass:"mdl-modal--colorpicker",image:".colorpicker__image",handle:".colorpicker__handle",slider:".colorpicker__brightness",background:".colorpicker__background",colorpicker:".colorpicker",button:".colorpicker__buttons > button"},notify:".mdl-notify__container",notifyHidden:"mdl-notify--hidden",notifyTemplateOffline:"template-offline-notify"}); \ No newline at end of file +!function(a){"use strict";function b(a){var b=document.createElement("div");return b.innerHTML=a,b.childNodes}function c(a,b){b instanceof NodeList?[].slice.call(b).forEach(function(b){a.appendChild(b)}):a.appendChild(b)}function d(a){var b=a,c=void 0!==b.type?b.type:"GET",d=void 0!==b.data?b.data:"",e=void 0!==b.headers?b.headers:{},f=new XMLHttpRequest;f.open(c,b.url,!0);for(var g in e)f.setRequestHeader(g,e[g]);return f.onload=function(){if(f.status<200||f.status>400)return void("function"==typeof b.error&&b.error(f));"function"==typeof b.callback&&b.callback(f)},f.onerror=function(){"function"==typeof b.error&&b.error(f)},f.send(d),f}function e(a,b){return document.getElementById(a).innerHTML.replace(/\{([\w]+)\}/,function(a,c){return void 0!==b[c]?b[c]:""})}function f(){var a=this;a.level=0,a.push=function(b){a.level++,history.pushState({page:b},document.title.textContent)},a.replace=function(a,b){history.replaceState({page:a},document.title.textContent,b)},window.addEventListener("popstate",function(b){a.level--,D.UI.navigate(b.state.page,!1)},!1)}function g(d){function f(a){a=a||window.event,27===a.keyCode&&i.hide()}function g(){document.addEventListener("keydown",f)}function h(){void 0!==i.onHide&&i.onHide(),document.removeEventListener("keydown",f)}var i=this;i.templateId="template-modal",i.text=e(i.templateId,{content:d}),i.show=function(){c(document.body,b(i.text)),i.bg=document.querySelector(a.modal),i.container=i.bg.querySelector(a.modalContainer),D.UI.currentModal=i,i.bg.addEventListener("click",function(){i.hide()}),i.container.addEventListener("click",function(a){a.stopPropagation()}),g()},i.hide=function(){document.body.querySelector(a.modal).remove(),delete D.UI.currentModal,h()}}function h(a,b){var c,d=this,e=!1;d.lock=!1,d.call=function(){c=arguments,d.lock||(e=!1,a.apply(null,c),d.lock=!0,setTimeout(function(){e||a.apply(null,c),d.lock=!1},b))},d.finish=function(){e=!0}}function i(a,b){function c(a){a.widget.setVisible(a.visibility)}var d=this;d.queue=[],d.timeout=null,d.processEvents=function(){for(d.timeout=null;0!==d.queue.length;)c(d.queue[0]),d.queue=d.queue.slice(1)},d.push=function(c){d.queue.length>b||(d.queue.push(c),null===d.timeout?d.timeout=setTimeout(d.processEvents,a):(clearTimeout(d.timeout),d.timeout=setTimeout(d.processEvents,a)))}}function j(b){var c=this,d=!1;c.parentNode=b,c.formRow=b.parentNode,c.item=c.parentNode.getAttribute(a.itemAttribute),c.id=c.parentNode.getAttribute(a.idAttribute),c.icon=c.parentNode.parentNode.querySelector(a.formIcon),c.visible=!c.formRow.classList.contains(a.formRowHidden),c.label=c.parentNode.parentNode.querySelector(a.formLabel),null!==c.icon&&(c.iconName=c.icon.getAttribute(a.iconAttribute)),c.reloadIcon=function(a){null!==c.icon&&c.icon.setAttribute("src","/icon/"+c.iconName+"?state="+encodeURIComponent(a)+"&format="+D.UI.iconType)},c.setVisible=function(b){b?c.formRow.classList.remove(a.formRowHidden):c.formRow.classList.add(a.formRowHidden),c.visible=b},c.setValue=function(a,b){c.reloadIcon(b),d?d=!1:c.setValuePrivate(a,b)},c.setValuePrivate=function(){},c.setLabel=function(){},c.suppressUpdate=function(){d=!0},c.setLabelColor=function(a){null!==c.label&&(c.label.style.color=a)},c.setValueColor=function(a){c.parentNode.style.color=a}}function k(b){var c=this;c.parentNode=b,c.id=c.parentNode.getAttribute(a.idAttribute),c.visible=!c.parentNode.classList.contains(a.formHidden),c.title=c.parentNode.querySelector(a.formTitle),c.setVisible=function(b){b?c.parentNode.classList.remove(a.formHidden):c.parentNode.classList.add(a.formHidden),c.visible=b},c.setLabel=function(a){c.title.innerHTML=a},c.setValue=function(){},c.suppressUpdate=function(){}}function l(a,b){b&&j.call(this,a);var c=this;if(c.image=a.querySelector("img"),c.updateInterval=parseInt(a.getAttribute("data-update-interval"),10),c.url=a.getAttribute("data-proxied-url"),c.validUrl="true"===a.getAttribute("data-valid-url"),c.setValuePrivate=function(a,b){b.startsWith("data:")?c.image.setAttribute("src",b):"UNDEF"!==b||c.validUrl?c.image.setAttribute("src",c.url+"&t="+Date.now()):c.image.setAttribute("src","images/none.png")},0!==c.updateInterval){c.updateInterval<100&&(c.updateInterval=100);var d=setInterval(function(){if(0===c.image.clientWidth)return void clearInterval(d);c.image.setAttribute("src",c.url+"&t="+Date.now())},c.updateInterval)}}function m(a){j.call(this,a);var b=this;b.hasValue="true"===b.parentNode.getAttribute("data-has-value"),b.setValuePrivate=function(c){b.hasValue&&(a.innerHTML=c)}}function n(b){j.call(this,b);var c=this;c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.value=c.parentNode.parentNode.querySelector(a.formValue),c.count=1*c.parentNode.getAttribute("data-count"),c.suppressUpdateButtons=!1,c.reset=function(){c.buttons.forEach(function(b){b.classList.remove(a.buttonActiveClass)})},c.onclick=function(){var b=this.getAttribute("data-value")+"";1!==c.count&&(c.reset(),this.classList.add(a.buttonActiveClass)),c.parentNode.dispatchEvent(C("control-change",{item:c.item,value:b})),c.suppressUpdateButtons=!0},c.valueMap={},c.buttons=[].slice.call(c.parentNode.querySelectorAll(a.controlButton)),c.setValuePrivate=function(b,d){if(c.hasValue&&(c.value.innerHTML=b),1!==c.count){if(c.suppressUpdateButtons)return void(c.suppressUpdateButtons=!1);c.reset(),void 0!==c.valueMap&&void 0!==c.valueMap[d]&&c.valueMap[d].classList.add(a.buttonActiveClass)}},c.setValueColor=function(a){c.value.style.color=a},c.buttons.forEach.call(c.buttons,function(a){var b=a.getAttribute("data-value")+"";c.valueMap[b]=a,a.addEventListener("click",c.onclick)})}function o(b){function c(a){if(a.stopPropagation(),"input"===a.target.tagName.toLowerCase()){var b=a.target.getAttribute("value");d.parentNode.dispatchEvent(C("control-change",{item:d.item,value:b})),setTimeout(function(){d.modal.hide()},300)}}j.call(this,b);var d=this,e=b.getAttribute("data-value-map");d.value=null,d.valueNode=b.parentNode.querySelector(a.formValue),d.valueMap=null!==e?JSON.parse(e):{},d.showModal=function(){var b=d.parentNode.querySelector(a.selectionRows).innerHTML;d.modal=new g(b),d.modal.show();var e=[].slice.call(d.modal.container.querySelectorAll(a.formRadio));if(null!==d.value){[].slice.call(d.modal.container.querySelectorAll("input[type=radio]")).forEach(function(a){a.value===d.value?a.checked=!0:a.checked=!1})}e.forEach(function(a){componentHandler.upgradeElement(a,"MaterialRadio"),a.addEventListener("click",c)})},d.setValuePrivate=function(a,b){d.value=""+b,void 0!==d.valueMap[b]?d.valueNode.innerHTML=D.UI.escapeHtml(d.valueMap[b]):d.valueNode.innerHTML=""},d.setValueColor=function(a){d.valueNode.style.color=a},d.parentNode.parentNode.addEventListener("click",d.showModal)}function p(b){function c(a){i.parentNode.dispatchEvent(C("control-change",{item:i.item,value:a}))}function d(a,b){g=!1,l=!0,h=setTimeout(function(){g=!0,c(a)},k),b.stopPropagation(),b.preventDefault()}function e(a,b){clearTimeout(h),l&&(l=!1,c(g?"STOP":a),b.stopPropagation(),b.preventDefault())}function f(a){c("STOP"),a.stopPropagation(),a.preventDefault()}j.call(this,b);var g,h,i=this,k=300,l=!1;i.buttonUp=i.parentNode.querySelector(a.rollerblind.up),i.buttonDown=i.parentNode.querySelector(a.rollerblind.down),i.buttonStop=i.parentNode.querySelector(a.rollerblind.stop),i.hasValue="true"===i.parentNode.getAttribute("data-has-value"),i.valueNode=i.parentNode.parentNode.querySelector(a.formValue),i.setValuePrivate=function(a){i.hasValue&&("DOWN"===a?a="100":"UP"===a&&(a="0"),i.valueNode.innerHTML=a)};var m=e.bind(null,"UP"),n=d.bind(null,"UP"),o=e.bind(null,"DOWN"),p=d.bind(null,"DOWN");i.buttonUp.addEventListener("touchstart",n),i.buttonUp.addEventListener("mousedown",n),i.buttonUp.addEventListener("touchend",m),i.buttonUp.addEventListener("mouseup",m),i.buttonUp.addEventListener("mouseleave",m),i.buttonDown.addEventListener("touchstart",p),i.buttonDown.addEventListener("mousedown",p),i.buttonDown.addEventListener("touchend",o),i.buttonDown.addEventListener("mouseup",o),i.buttonDown.addEventListener("mouseleave",o),i.buttonStop.addEventListener("mousedown",f),i.buttonStop.addEventListener("touchstart",f)}function q(b){function c(a,b){var c=d.value+(!0===a?d.step:-d.step);c=c>d.max?d.max:c,c=cc*c){var h=1-Math.abs(c/Math.sqrt(g));b.x-=d*h,b.y-=e*h}p.handle.style.left=b.x/p.image.clientWidth*100+"%",p.handle.style.top=b.y/p.image.clientWidth*100+"%";var i=d>=0?(2*Math.PI-Math.atan(e/d)+Math.PI/2)/(2*Math.PI):(2*Math.PI-Math.atan(e/d)-Math.PI/2)/(2*Math.PI),j={h:isNaN(i)?0:i,s:Math.sqrt(g)/c,v:1},k=f(j);p.hsvValue={h:j.h,s:j.s,v:p.slider.value/100},k.l=k.l<.5?.5:k.l,p.background.style.background="hsl("+360*k.h+", 100%, "+Math.round(100*k.l)+"%)"}function i(a){var b=e(a);p.slider.value=100*b.v;var c=50+Math.round(b.s*Math.cos(2*Math.PI*b.h)*50),d=50+Math.round(b.s*Math.sin(2*Math.PI*b.h)*50);p.handle.style.top=c+"%",p.handle.style.left=d+"%",b.v=1;var f=r.hsv2rgb(b);p.background.style.background="rgb("+Math.round(f.r)+","+Math.round(f.g)+","+Math.round(f.b)+")"}function j(){null!==p.interval&&(clearInterval(p.interval),p.interval=null),p.isBeingChanged=!1,window.removeEventListener("mouseup",j)}function k(a){p.interval=setInterval(function(){d(p.hsvValue)},300),window.addEventListener("mouseup",j),g(a),d(p.hsvValue),p.isBeingChanged=!0,a.stopPropagation()}function l(a){(void 0!==a.touches||1&a.buttons)&&(g(a),a.stopPropagation(),a.preventDefault())}function m(a){null!==p.interval&&(clearInterval(p.interval),p.interval=null),window.removeEventListener("mouseup",j),p.isBeingChanged=!1,a.stopPropagation()}function n(){p.hsvValue.v=p.slider.value/100,p.debounceProxy.call()}function o(){p.hsvValue.v=p.slider.value/100,p.debounceProxy.call(),p.debounceProxy.finish()}var p=this;p.container=b,p.value=c,p.hsvValue=e(c),p.interval=null,p.isBeingChanged=!1,p.colorpicker=p.container.querySelector(a.colorpicker.colorpicker),p.image=p.container.querySelector(a.colorpicker.image),p.background=p.container.querySelector(a.colorpicker.background),p.handle=p.container.querySelector(a.colorpicker.handle),p.slider=p.container.querySelector(a.colorpicker.slider),p.button=p.container.querySelector(a.controlButton),componentHandler.upgradeElement(p.button,"MaterialButton"),componentHandler.upgradeElement(p.button,"MaterialRipple"),p.debounceProxy=new h(function(){d(p.hsvValue)},200),p.updateColor=function(a){p.isBeingChanged||i(a)},p.slider.addEventListener("change",n),p.slider.addEventListener("touchend",o),p.slider.addEventListener("mouseup",o),p.image.addEventListener("mousedown",l),p.image.addEventListener("mousemove",l),p.image.addEventListener("touchmove",l),p.image.addEventListener("touchstart",l),p.image.addEventListener("touchend",m),p.image.addEventListener("mouseup",m),p.image.addEventListener("mousedown",k),p.image.addEventListener("touchstart",k),i(c)}function s(b){function c(a){return{r:parseInt(a.substr(1,2),16),g:parseInt(a.substr(3,2),16),b:parseInt(a.substr(5,2),16)}}function d(a){l.parentNode.dispatchEvent(C("control-change",{item:l.item,value:a}))}function f(a){l.pressed=!0,l.longPress=!1,k=setInterval(function(){l.longPress=!0,d(a)},m)}function h(a){l.pressed&&(l.longPress||d(a),l.pressed=!1,l.longPress=!1,clearInterval(k))}function i(){l.modal=new g(e("template-colorpicker")),l.modal.show(),l.modal.container.classList.add(a.colorpicker.modalClass),l.modal.onHide=function(){l.modalControl=null,l.modal=null},l.modalControl=new r(l.modal.container,l.value,function(a){l.value=r.hsv2rgb(a),d(Math.round(360*a.h%360)+","+Math.round(100*a.s%100)+","+Math.round(100*a.v))}),l.modal.container.querySelector(a.colorpicker.button).addEventListener("click",function(){l.modal.hide()})}j.call(this,b);var k,l=this,m=300;l.value=c(l.parentNode.getAttribute("data-value")),l.modalControl=null,l.buttonUp=l.parentNode.querySelector(a.colorpicker.up),l.buttonDown=l.parentNode.querySelector(a.colorpicker.down),l.buttonPick=l.parentNode.querySelector(a.colorpicker.pick),l.longPress=!1,l.pressed=!1,l.setValue=function(a){var b=a.split(","),c={h:b[0]/360,s:b[1]/100,v:b[2]/100};l.value=r.hsv2rgb(c),null!==l.modalControl&&l.modalControl.updateColor(l.value)};var n=f.bind(null,"INCREASE"),o=f.bind(null,"DECREASE"),p=h.bind(null,"ON"),q=h.bind(null,"OFF");l.buttonUp.addEventListener("touchstart",n),l.buttonUp.addEventListener("mousedown",n),l.buttonUp.addEventListener("mouseleave",p),l.buttonUp.addEventListener("touchend",p),l.buttonUp.addEventListener("mouseup",p),l.buttonDown.addEventListener("touchstart",o),l.buttonDown.addEventListener("mousedown",o),l.buttonDown.addEventListener("touchend",q),l.buttonDown.addEventListener("mouseup",q),l.buttonDown.addEventListener("mouseleave",q),l.buttonPick.addEventListener("click",i)}function t(b){j.call(this,b);var c=this;c.input=c.parentNode.querySelector("input[type=checkbox]"),c.input.addEventListener("change",function(){c.parentNode.dispatchEvent(C("control-change",{item:c.item,value:c.input.checked?"ON":"OFF"}))}),c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.valueNode=c.parentNode.parentNode.querySelector(a.formValue),c.setValuePrivate=function(a,b){var d="ON"===b;c.input.checked!==d&&(c.input.checked=d,d?c.parentNode.MaterialSwitch.on():c.parentNode.MaterialSwitch.off()),c.hasValue&&(c.valueNode.innerHTML=a)},c.setValueColor=function(a){c.valueNode.style.color=a}}function u(b){function c(){f.parentNode.dispatchEvent(C("control-change",{item:f.item,value:f.input.value}))}function d(){null!==g&&clearTimeout(g),f.locked=!0,D.changeListener.pause()}function e(){f.debounceProxy.call(),setTimeout(function(){D.changeListener.resume()},5),g=setTimeout(function(){f.locked=!1},300),f.debounceProxy.finish()}j.call(this,b);var f=this;f.input=f.parentNode.querySelector("input[type=range]"),f.hasValue="true"===f.parentNode.getAttribute("data-has-value"),f.valueNode=f.parentNode.parentNode.querySelector(a.formValue),f.locked=!1,function(){var a=parseInt(f.input.getAttribute("data-state"),10);isNaN(a)?f.input.value=0:f.input.value=a,f.input.MaterialSlider&&f.input.MaterialSlider.change()}(),f.debounceProxy=new h(function(){c()},200),f.input.addEventListener("change",function(){f.debounceProxy.call()}),f.setValuePrivate=function(a,b){if(f.hasValue&&(f.valueNode.innerHTML=a),f.locked)return void f.reloadIcon(b);f.input.value=b,f.input.MaterialSlider.change()},f.setValueColor=function(a){f.valueNode.style.color=a};var g=null;f.input.addEventListener("touchstart",d),f.input.addEventListener("mousedown",d),f.input.addEventListener("touchend",e),f.input.addEventListener("mouseup",e)}function v(b){j.call(this,b);var c=this;c.target=b.getAttribute("data-target"),c.hasValue="true"===c.parentNode.getAttribute("data-has-value"),c.container=b.parentNode.querySelector(a.formValue),c.setValuePrivate=function(a){c.hasValue&&(c.container.innerHTML=a)},c.setValueColor=function(a){c.container.style.color=a},b.parentNode.addEventListener("click",function(){D.UI.navigate(c.target,!0)})}function w(a){d({type:"POST",url:"/rest/items/"+a.detail.item,data:a.detail.value,headers:{"Content-Type":"text/plain"}})}function x(b){function c(a){var b=a.documentElement,c=[];if("page"===b.tagName){[].forEach.call(b.childNodes,function(a){a instanceof Text||c.push(a)}),g.setTitle(c[0].textContent);for(var d=document.querySelector(".page-content");d.firstChild;)d.removeChild(d.firstChild);d.insertAdjacentHTML("beforeend",c[1].textContent)}}var e={Loading:1,Idle:2},g=this,h=e.Idle,i=new f;g.page=document.body.getAttribute("data-page-id"),g.sitemap=document.body.getAttribute("data-sitemap"),g.destination=null,g.root=b,g.loading=g.root.querySelector(a.uiLoadingBar),g.layoutTitle=document.querySelector(a.layoutTitle),g.iconType=document.body.getAttribute(a.iconTypeAttribute),g.notification=document.querySelector(a.notify),g.escapeHtml=function(a){for(var b=a,c=[[/&/g,"&"],[//g,">"]],d=0;d0?history.back():location.href=location.origin+"/basicui/app?sitemap="+D.UI.sitemap})}()}function y(){this.paused=!1,this.pause=function(){this.paused=!0},this.resume=function(){this.paused=!1},this.extractValueFromLabel=function(a){var b=null;if("string"==typeof a&&-1!==a.indexOf("[")&&-1!==a.indexOf("]")){var c=a.indexOf("[")+1;b=a.substr(c,a.lastIndexOf("]")-c)}return b},this.getTitleFromLabel=function(a){var b=this.extractValueFromLabel(a),c=null;return null!==b&&(c=a.substr(0,a.indexOf("["))+b),c}}function z(a){y.call(this);var b=this;b.navigate=function(){},b.source=new EventSource(a),b.source.addEventListener("event",function(a){if(!b.paused){var c,d,e=JSON.parse(a.data);if("SITEMAP_CHANGED"===e.TYPE){var f=window.location.href,g=f.split("?");return g.length>1?window.location.href=g[0]+"?sitemap="+e.sitemapName:window.location.reload(!0),void b.pause()}if(e.widgetId in D.dataModel||e.widgetId===D.UI.page)if(c=b.extractValueFromLabel(e.label),null===c&&(c=e.item.state),d=b.getTitleFromLabel(e.label),e.widgetId===D.UI.page&&null!==d)D.UI.setTitle(D.UI.escapeHtml(d));else if(void 0!==D.dataModel[e.widgetId]){var h=D.dataModel[e.widgetId];h.visible!==e.visibility&&D.UI.layoutChangeProxy.push({widget:h,visibility:e.visibility}),h.setValue(D.UI.escapeHtml(c),e.item.state),[{apply:h.setLabel,data:e.label,fallback:null},{apply:h.setLabelColor,data:e.labelcolor,fallback:""},{apply:h.setValueColor,data:e.valuecolor,fallback:""}].forEach(function(a){void 0!==a.data?a.apply(a.data):null!==a.fallback&&a.apply(a.fallback)})}}}),b.source.onerror=function(){b.source.close(),b.connectionError()}}function A(){function a(a){function b(a){a.forEach(function(a){if(void 0!==a.item){var b=a.item.name,d=a.item.state,e=a.label,f=c.extractValueFromLabel(a.label),g=a.labelcolor,h=a.valuecolor;null===f&&(f=d),void 0!==D.dataModelLegacy[b]&&D.dataModelLegacy[b].widgets.forEach(function(a){"NULL"!==d&&a.setValue(D.UI.escapeHtml(f),d),void 0!==e&&a.setLabel(e),void 0!==g?a.setLabelColor(g):a.setLabelColor(""),void 0!==h?a.setValueColor(h):a.setValueColor("")})}})}var d;try{a=JSON.parse(a)}catch(e){return}d=c.getTitleFromLabel(a.title),null!==d&&D.UI.setTitle(D.UI.escapeHtml(d)),a.leaf?b(a.widgets):a.widgets.forEach(function(a){b(a.widgets)})}function b(){var e=Math.random().toString(16).slice(2);c.request=d({url:"/rest/sitemaps/"+c.sitemap+"/"+c.page+"?_="+e,headers:{"X-Atmosphere-Transport":"long-polling"},callback:function(d){c.paused||a(d.responseText),setTimeout(function(){b()},1)},error:function(){setTimeout(function(){b()},1e3)}})}y.call(this);var c=this;c.sitemap=document.body.getAttribute("data-sitemap"),c.page=document.body.getAttribute("data-page-id"),c.navigate=function(a){c.request.abort(),c.page=a,b()},c.pause=function(){c.request.abort(),c.paused=!0},c.resume=function(){c.paused=!1,b()},b()}function B(){function b(a){E.eventSource?z.call(f,a):A.call(f)}function c(){f.startSubscriber(f.subscribeResponse),f.subscribeResponse=null}var f=this;f.subscribeRequestURL="/rest/sitemaps/events/subscribe",f.reconnectInterval=null,f.subscribeResponse=null,f.suppressErrorsState=!1,f.connectionRestored=function(a){clearInterval(f.reconnectInterval),f.navigate=c,f.subscribeResponse=a,D.UI.hideNotification(),D.UI.navigate(D.UI.page,!1)},f.connectionError=function(){if(!f.suppressErrorsState){var b=e(a.notifyTemplateOffline,{});D.UI.showNotification(b),f.reconnectInterval=setInterval(function(){d({url:f.subscribeRequestURL,type:"POST",callback:f.connectionRestored})},1e4)}},f.suppressErrors=function(){f.suppressErrorsState=!0},f.startSubscriber=function(a){var c,d,e,f,g,h;try{c=JSON.parse(a.responseText)}catch(i){return}if("CREATED"===c.status){try{d=c.context.headers.Location[0]}catch(i){return}e=d.split("/"),g=e[e.length-1],f=document.body.getAttribute("data-sitemap"),h=document.body.getAttribute("data-page-id"),D.subscriptionId=g,b(d+"?sitemap="+f+"&pageid="+h)}},d({url:f.subscribeRequestURL,type:"POST",callback:f.startSubscriber})}var C,D={dataModel:{}},E={eventLayerXY:function(){var a;return a=void 0===document.createEvent?new MouseEvent(null):document.createEvent("MouseEvent"),void 0!==a.layerX}(),pointerEvents:void 0!==document.createElement("div").style.pointerEvents,customEvent:function(){var a=!0;try{new CustomEvent("event",{})}catch(b){a=!1}return a}(),elementRemove:void 0!==Element.prototype.remove,flexbox:void 0!==document.createElement("div").style.flexBasis,flexboxLegacy:function(){var a=document.createElement("div");return void 0!==a.style.boxDirection||void 0!==a.style.webkitBoxDirection}(),eventSource:"EventSource"in window};!function(){C=E.customEvent?function(a,b){return new CustomEvent(a,{detail:b})}:function(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!0,!1,b),c},E.elementRemove||(Element.prototype.remove=function(){this.parentNode.removeChild(this)}),!E.flexbox&&E.flexboxLegacy&&document.documentElement.classList.add("flexbox-legacy")}(),r.hsv2rgb=function(a){var b,c,d,e=a.h,f=a.s,g=a.v,h=Math.floor(6*e),i=6*e-h,j=g*(1-f),k=g*(1-i*f),l=g*(1-(1-i)*f);switch(h%6){case 0:b=g,c=l,d=j;break;case 1:b=k,c=g,d=j;break;case 2:b=j,c=g,d=l;break;case 3:b=j,c=k,d=g;break;case 4:b=l,c=j,d=g;break;case 5:b=g,c=j,d=k}return{r:255*b,g:255*c,b:255*d}},document.addEventListener("DOMContentLoaded",function(){D.UI=new x(document),D.UI.layoutChangeProxy=new i(100,50),D.UI.initControls(),D.changeListener=new B,window.addEventListener("beforeunload",function(){D.changeListener.suppressErrors()})})}({itemAttribute:"data-item",idAttribute:"data-widget-id",iconAttribute:"data-icon",iconTypeAttribute:"data-icon-type",controlButton:"button",buttonActiveClass:"mdl-button--accent",modal:".mdl-modal",modalContainer:".mdl-modal__content",selectionRows:".mdl-form__selection-rows",form:".mdl-form",formTitle:".mdl-form__title",formHidden:"mdl-form--hidden",formControls:".mdl-form__control",formRowHidden:"mdl-form__row--hidden",formValue:".mdl-form__value",formRadio:".mdl-radio",formRadioControl:".mdl-radio__button",formIcon:".mdl-form__icon img",formLabel:".mdl-form__label",uiLoadingBar:".ui__loading",layoutTitle:".mdl-layout-title",layoutHeader:".mdl-layout__header",backButton:".navigation__button-back",rollerblind:{up:".mdl-form__rollerblind--up",down:".mdl-form__rollerblind--down",stop:".mdl-form__rollerblind--stop"},setpoint:{up:".mdl-form__setpoint--up",down:".mdl-form__setpoint--down"},colorpicker:{up:".mdl-form__colorpicker--up",down:".mdl-form__colorpicker--down",pick:".mdl-form__colorpicker--pick",modalClass:"mdl-modal--colorpicker",image:".colorpicker__image",handle:".colorpicker__handle",slider:".colorpicker__brightness",background:".colorpicker__background",colorpicker:".colorpicker",button:".colorpicker__buttons > button"},notify:".mdl-notify__container",notifyHidden:"mdl-notify--hidden",notifyTemplateOffline:"template-offline-notify"}); \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.classic/META-INF/MANIFEST.MF b/extensions/ui/org.eclipse.smarthome.ui.classic/META-INF/MANIFEST.MF index 146426632e7..f938101d191 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.classic/META-INF/MANIFEST.MF +++ b/extensions/ui/org.eclipse.smarthome.ui.classic/META-INF/MANIFEST.MF @@ -27,12 +27,5 @@ Import-Package: javax.servlet, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.ui.classic Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/webappservlet.xml, OSGI-INF/pagerenderer.xml, - OSGI-INF/cmdservlet.xml, OSGI-INF/grouprenderer.xml, OSGI-INF/framerenderer.xml, - OSGI-INF/switchrenderer.xml, OSGI-INF/selectionrenderer.xml, - OSGI-INF/listrenderer.xml, OSGI-INF/textrenderer.xml, - OSGI-INF/imagerenderer.xml, OSGI-INF/sliderrenderer.xml, - OSGI-INF/chartrenderer.xml, OSGI-INF/videorenderer.xml, - OSGI-INF/webviewrenderer.xml, OSGI-INF/setpointrenderer.xml, - OSGI-INF/colorpickerrenderer.xml, OSGI-INF/mapviewrenderer.xml +Service-Component: OSGI-INF/*.xml Export-Package: org.eclipse.smarthome.ui.classic.render diff --git a/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/WebAppActivator.java b/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/WebAppActivator.java index 64ff088a396..ce9d59f9177 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/WebAppActivator.java +++ b/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/WebAppActivator.java @@ -12,6 +12,8 @@ /** * Extension of the default OSGi bundle activator + * + * @author Kai Kreuzer - Initial contribution */ public final class WebAppActivator implements BundleActivator { diff --git a/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/render/MapviewRenderer.java b/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/render/MapviewRenderer.java index c5f5674b96c..a52a83121e3 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/render/MapviewRenderer.java +++ b/extensions/ui/org.eclipse.smarthome.ui.classic/src/main/java/org/eclipse/smarthome/ui/classic/internal/render/MapviewRenderer.java @@ -18,41 +18,43 @@ /** * This is an implementation of the {@link WidgetRenderer} interface, which * can produce HTML code for Text widgets. - * + * * @author Gaël L'hopital - Initial contribution * */ public class MapviewRenderer extends AbstractWidgetRenderer { - public boolean canRender(Widget w) { - return w instanceof Mapview; - } - - public EList renderWidget(Widget w, StringBuilder sb) throws RenderException { - Mapview mapview = (Mapview) w; - String snippet = getSnippet("mapview"); - - State state = itemUIRegistry.getState(mapview); - if(state instanceof PointType) { - PointType pointState = (PointType) state; - double latitude = pointState.getLatitude().doubleValue(); - double longitude = pointState.getLongitude().doubleValue(); - snippet = StringUtils.replace(snippet, "%lat%", Double.toString(latitude)); - snippet = StringUtils.replace(snippet, "%lon%", Double.toString(longitude)); - snippet = StringUtils.replace(snippet, "%lonminus%", Double.toString(longitude-0.01)); - snippet = StringUtils.replace(snippet, "%lonplus%", Double.toString(longitude+0.01)); - snippet = StringUtils.replace(snippet, "%latminus%", Double.toString(latitude-0.01)); - snippet = StringUtils.replace(snippet, "%latplus%", Double.toString(latitude+0.01)); - } - - int height = mapview.getHeight(); - if(height==0) { - height = 4; // set default height to something viewable - } - height = height * 36; - snippet = StringUtils.replace(snippet, "%height%", Integer.toString(height)); - - sb.append(snippet); - return null; - } -} \ No newline at end of file + @Override + public boolean canRender(Widget w) { + return w instanceof Mapview; + } + + @Override + public EList renderWidget(Widget w, StringBuilder sb) throws RenderException { + Mapview mapview = (Mapview) w; + String snippet = getSnippet("mapview"); + + State state = itemUIRegistry.getState(mapview); + if (state instanceof PointType) { + PointType pointState = (PointType) state; + double latitude = pointState.getLatitude().doubleValue(); + double longitude = pointState.getLongitude().doubleValue(); + snippet = StringUtils.replace(snippet, "%lat%", Double.toString(latitude)); + snippet = StringUtils.replace(snippet, "%lon%", Double.toString(longitude)); + snippet = StringUtils.replace(snippet, "%lonminus%", Double.toString(longitude - 0.01)); + snippet = StringUtils.replace(snippet, "%lonplus%", Double.toString(longitude + 0.01)); + snippet = StringUtils.replace(snippet, "%latminus%", Double.toString(latitude - 0.01)); + snippet = StringUtils.replace(snippet, "%latplus%", Double.toString(latitude + 0.01)); + } + + int height = mapview.getHeight(); + if (height == 0) { + height = 4; // set default height to something viewable + } + height = height * 36; + snippet = StringUtils.replace(snippet, "%height%", Integer.toString(height)); + + sb.append(snippet); + return null; + } +} diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/META-INF/MANIFEST.MF b/extensions/ui/org.eclipse.smarthome.ui.paper/META-INF/MANIFEST.MF index 6787609b19c..259da4038c4 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/META-INF/MANIFEST.MF +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/META-INF/MANIFEST.MF @@ -9,5 +9,5 @@ Import-Package: org.osgi.service.component, org.slf4j Bundle-SymbolicName: org.eclipse.smarthome.ui.paper;singleton:=true Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/* +Service-Component: OSGI-INF/*.xml Bundle-ClassPath: patch/,. diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/gulpfile.js b/extensions/ui/org.eclipse.smarthome.ui.paper/gulpfile.js index f534866b723..5064bd0b5ce 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/gulpfile.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/gulpfile.js @@ -54,7 +54,8 @@ var paths = { './node_modules/angular-material/angular-material.min.js', './node_modules/angular-messages/angular-messages.min.js', './node_modules/angular-sanitize/angular-sanitize.min.js', - './node_modules/angular-ui-sortable/dist/sortable.min.js' + './node_modules/angular-ui-sortable/dist/sortable.min.js', + './node_modules/angular-material-expansion-panel/dist/md-expansion-panel.min.js', ], 'name': 'angular-bundle.js' }], @@ -83,6 +84,7 @@ var paths = { CSSLibs: [ './node_modules/bootstrap/dist/css/bootstrap.min.css', './node_modules/angular-material/angular-material.min.css', + './node_modules/angular-material-expansion-panel/dist/md-expansion-panel.min.css' ], FontLibs: [ './node_modules/roboto-fontface/fonts/*.woff', @@ -212,6 +214,7 @@ gulp.task('inject', ['build'], function () { './web-src/js/app.js', './web-src/js/constants.js', './web-src/js/controllers.configuration.js', + './web-src/js/controllers.configuration.bindings.js', './web-src/js/controllers.system.js', './web-src/js/controllers.items.js', './web-src/js/controllers.control.js', diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/package.json b/extensions/ui/org.eclipse.smarthome.ui.paper/package.json index 7d2f83fe3cb..dc72e9f2134 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/package.json +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/package.json @@ -12,24 +12,14 @@ "angular-animate": "1.4.8", "angular-aria": "1.4.8", "angular-material": "1.0.1", + "angular-material-expansion-panel": "0.7.2", "angular-messages": "1.4.8", "angular-resource": "1.4.8", "angular-route": "1.4.8", "angular-sanitize": "1.4.8", "angular-ui-sortable": "0.13.4", "bootstrap": "3.3.2", - "browser-sync": "~2.7.12", - "del": "^2.0.0", "eventsource-polyfill": "^0.9.6", - "gulp": "^3.9.0", - "gulp-angular-filesort": "~1.1.1", - "gulp-concat": "^2.6.0", - "gulp-inject": "^4.0.0", - "gulp-ng-annotate": "~1.0.0", - "gulp-rename": "^1.2.2", - "gulp-uglify": "^1.5.1", - "gulp-util": "^3.0.7", - "http-proxy-middleware": "^0.9.0", "jquery": "2.1.3", "jquery-ui": "^1.11.2", "masonry-layout": "3.3.1", @@ -43,6 +33,17 @@ }, "devDependencies": { "angular-mocks": "1.4.8", + "browser-sync": "~2.7.12", + "del": "^2.0.0", + "gulp": "^3.9.0", + "gulp-angular-filesort": "~1.1.1", + "gulp-concat": "^2.6.0", + "gulp-inject": "^4.0.0", + "gulp-ng-annotate": "~1.0.0", + "gulp-rename": "^1.2.2", + "gulp-uglify": "^1.5.1", + "gulp-util": "^3.0.7", + "http-proxy-middleware": "^0.9.0", "jasmine-core": "^2.5.2", "karma": "^1.1.0", "karma-angular": "0.0.6", diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configService.test.js b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configService.test.js index 2ca464f413c..ffc85dede4a 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configService.test.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configService.test.js @@ -213,6 +213,21 @@ describe('factory configService', function() { expect(paramsDecimal[0].parameters[0].element).toEqual("select"); expect(paramsDecimal[0].parameters[0].options[0].value).toEqual(1); }); + it('should return parse floats type DECIMAL with options', function() { + var inputParams = [ { + type : 'decimal', + options : [ { + value : "1.1" + }, { + value : "2.2" + } ], + limitToOptions : true + } ]; + var params = configService.getRenderingModel(inputParams); + expect(params[0].parameters[0].element).toEqual("select"); + expect(params[0].parameters[0].options[0].value).toEqual(1.1); + expect(params[0].parameters[0].options[1].value).toEqual(2.2); + }); it('should return text widget for type INTEGER/DECIMAL', function() { var inputParams = [ { type : 'integer' @@ -225,6 +240,36 @@ describe('factory configService', function() { expect(paramsDecimal[0].parameters[0].element).toEqual("input"); expect(paramsDecimal[0].parameters[0].inputType).toEqual("number"); }); + it('should set defaults type DECIMAL', function() { + var thing = { + configuration: {} + } + + var thingType = { + configParameters : [ { + name: 'test', + type : 'DECIMAL', + defaultValue : '1.1' + } ] + } + + configService.setDefaults(thing, thingType); + expect(thing.configuration['test']).toEqual(1.1); + }); + it('should set config values from type DECIMAL', function() { + var originalConfiguration = {}; + + var groups = [{ + parameters : [ { + name : 'test', + type : 'DECIMAL', + defaultValue : '1.1' + } ] + }] + + var config = configService.setConfigDefaults(originalConfiguration, groups, false); + expect(config['test']).toEqual(1.1); + }); it('should rertieve rules and add options for context RULE', function() { var inputParams = [ { context : 'RULE' diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.binding.test.js b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.binding.test.js new file mode 100644 index 00000000000..703c5dc65bb --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.binding.test.js @@ -0,0 +1,73 @@ +describe('module PaperUI.controllers.configuration.bindings', function() { + beforeEach(function() { + module('PaperUI'); + }); + describe('tests for BindingController', function() { + var bindingController, scope, mdDialog; + beforeEach(inject(function($injector, $rootScope, $controller, $mdDialog) { + scope = $rootScope.$new(); + mdDialog = $mdDialog + $controller('BodyController', { + '$scope' : scope + }); + bindingController = $controller('BindingController', { + '$scope' : scope + }); + })); + it('should require BindingController', function() { + expect(bindingController).toBeDefined(); + }); + }); + + describe('tests for ConfigureBindingDialogController', function() { + var configureBindingDialogController, scope, bindingService; + var restConfig; + beforeEach(inject(function($injector, $rootScope, $controller) { + scope = $rootScope.$new(); + var bindingRepository = $injector.get('bindingRepository'); + restConfig = $injector.get('restConfig'); + $rootScope.data.bindings = [ { + id : 'B' + } ]; + configureBindingDialogController = $controller('ConfigureBindingDialogController', { + '$scope' : scope, + 'binding' : { + 'id' : 'B', + 'configDescriptionURI' : 'CDURI' + }, + 'bindingRepository' : bindingRepository + }); + $httpBackend = $injector.get('$httpBackend'); + $httpBackend.when('GET', restConfig.restPath + "/config-descriptions/CDURI").respond({ + parameters : [ { + type : 'input', + name : 'PNAME' + } ] + }); + $httpBackend.when('GET', restConfig.restPath + "/bindings/B/config").respond({ + PNAME : '1' + }); + $httpBackend.flush(); + bindingService = $injector.get('bindingService'); + })); + it('should require ConfigureBindingDialogController', function() { + expect(configureBindingDialogController).toBeDefined(); + }); + it('should get binding parameters', function() { + expect(scope.parameters.length).toEqual(1); + }); + it('should get binding configuration', function() { + expect(scope.configuration.PNAME).toEqual(1); + }); + it('should add binding parameter', function() { + scope.configArray = []; + scope.addParameter(); + expect(scope.configArray.length).toEqual(1); + }); + it('should save binding configuration', function() { + spyOn(bindingService, 'updateConfig'); + scope.save(); + expect(bindingService.updateConfig).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.test.js b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.test.js index 95315c10e5a..7a679697dcf 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.test.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/configuration.test.js @@ -2,7 +2,6 @@ describe('module PaperUI.controllers.configuration', function() { beforeEach(function() { module('PaperUI'); }); - var restConfig; describe('tests for ConfigurationPageController', function() { var configurationPageController, scope; beforeEach(inject(function($injector, $rootScope, $controller) { @@ -22,112 +21,6 @@ describe('module PaperUI.controllers.configuration', function() { expect(label).toEqual('1'); }); }); - describe('tests for BindingController', function() { - var bindingController, scope, mdDialog; - beforeEach(inject(function($injector, $rootScope, $controller, $mdDialog) { - scope = $rootScope.$new(); - mdDialog = $mdDialog - $controller('BodyController', { - '$scope' : scope - }); - bindingController = $controller('BindingController', { - '$scope' : scope - }); - })); - it('should require BindingController', function() { - expect(bindingController).toBeDefined(); - }); - it('should open binding info dialog', function() { - spyOn(mdDialog, 'show'); - scope.openBindingInfoDialog(0); - expect(mdDialog.show).toHaveBeenCalled(); - }); - it('should open binding configuration dialog', function() { - spyOn(mdDialog, 'show'); - scope.configure(0); - expect(mdDialog.show).toHaveBeenCalled(); - }); - }); - - describe('tests for BindingInfoDialogController', function() { - var bindingInfoDialogController, scope, mdDialog; - beforeEach(inject(function($injector, $rootScope, $controller) { - scope = $rootScope.$new(); - var bindingRepository = $injector.get('bindingRepository'); - var thingTypeRepository = $injector.get('thingTypeRepository'); - restConfig = $injector.get('restConfig'); - $rootScope.data.bindings = [ { - id : 'B' - } ]; - - $httpBackend = $injector.get('$httpBackend'); - $httpBackend.when('GET', restConfig.restPath + "/thing-types").respond([ { - UID : 'B:T' - } ]); - bindingInfoDialogController = $controller('BindingInfoDialogController', { - '$scope' : scope, - 'bindingId' : 'B', - 'bindingRepository' : bindingRepository, - 'thingTypeRepository' : thingTypeRepository - }); - $httpBackend.flush(); - })); - it('should require BindingInfoDialogController', function() { - expect(bindingInfoDialogController).toBeDefined(); - }); - it('should get ThingTypes of Binding', function() { - expect(scope.binding.thingTypes[0].UID).toEqual('B:T'); - }); - - }); - - describe('tests for ConfigureBindingDialogController', function() { - var configureBindingDialogController, scope, bindingService; - beforeEach(inject(function($injector, $rootScope, $controller) { - scope = $rootScope.$new(); - var bindingRepository = $injector.get('bindingRepository'); - $rootScope.data.bindings = [ { - id : 'B' - } ]; - configureBindingDialogController = $controller('ConfigureBindingDialogController', { - '$scope' : scope, - 'bindingId' : 'B', - 'configDescriptionURI' : 'CDURI', - 'bindingRepository' : bindingRepository - }); - $httpBackend = $injector.get('$httpBackend'); - $httpBackend.when('GET', restConfig.restPath + "/config-descriptions/CDURI").respond({ - parameters : [ { - type : 'input', - name : 'PNAME' - } ] - }); - $httpBackend.when('GET', restConfig.restPath + "/bindings/B/config").respond({ - PNAME : '1' - }); - $httpBackend.flush(); - bindingService = $injector.get('bindingService'); - })); - it('should require ConfigureBindingDialogController', function() { - expect(configureBindingDialogController).toBeDefined(); - }); - it('should get binding parameters', function() { - expect(scope.parameters.length).toEqual(1); - }); - it('should get binding configuration', function() { - expect(scope.configuration.PNAME).toEqual(1); - }); - it('should add binding parameter', function() { - scope.configArray = []; - scope.addParameter(); - expect(scope.configArray.length).toEqual(1); - }); - it('should save binding configuration', function() { - spyOn(bindingService, 'updateConfig'); - scope.save(); - expect(bindingService.updateConfig).toHaveBeenCalled(); - }); - }); describe('tests for ServicesController', function() { var ServicesController, scope, injector; diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/setup.test.js b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/setup.test.js index 9d43c775535..0291c77edd9 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/tests/setup.test.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/tests/setup.test.js @@ -294,16 +294,22 @@ describe('module PaperUI.controllers.setup', function() { $httpBackend = $injector.get('$httpBackend'); mdDialog = $injector.get('$mdDialog'); thingTypeRepository = $injector.get('thingTypeRepository'); - spyOn(thingTypeRepository, "getOne").and.callThrough(); + thingTypeRepository.singleElements = { + 'A:B' : { + UID : 'A:B', + label : 'LABEL' + } + } + spyOn(thingTypeRepository, 'getOne').and.callThrough(); rootScope.data.thingTypes = [ { - UID : "A:B", + UID : 'A:B', label : 'LABEL' } ]; approveInboxEntryDialogController = $controller('ApproveInboxEntryDialogController', { '$scope' : scope, 'discoveryResult' : { label : 'LABEL', - thingTypeUID : "A:B" + thingTypeUID : 'A:B' } }); discoveryService = $injector.get('discoveryService'); diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/components.css b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/components.css index fbf2c56545b..7b5eec76e86 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/components.css +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/components.css @@ -88,6 +88,7 @@ md-tabs { md-tabs md-tabs-wrapper { box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.15); background-color: #01324D; + z-index: 1; } md-tabs section { @@ -285,10 +286,15 @@ md-slider.md-default-theme.hue .md-thumb:after { } /* Fab Item / List Item */ -.fab-item, .list-item { +.list-item { position: relative; } +.fab-item { + margin-top: 15px; + margin-bottom: 15px; +} + .fab-item h3, .list-item h3 { font-size: 18px; font-weight: 500; @@ -304,17 +310,17 @@ md-slider.md-default-theme.hue .md-thumb:after { } .fab-item button.md-fab { - position: absolute; - left: 0px; - top: 0px; + float: left; + margin: 0; } .fab-item .item-content { - margin-left: 75px; min-height: 60px; + padding-left: 20px; + display: flex; } -.fab-item .item-content .actions, .list-item .actions, .item-content .actions +.list-item .actions { position: absolute; top: 7px; @@ -322,6 +328,14 @@ md-slider.md-default-theme.hue .md-thumb:after { background: white; } +.fab-item .item-content .actions { + width: 160px; +} + +.fab-item .item-content .actions i:last-child { + float: right; +} + .extensions .card .item-content { padding-top: 5px; } @@ -342,6 +356,17 @@ md-slider.md-default-theme.hue .md-thumb:after { margin-top: 10px; } +.item-content .description { + width: 100%; + display: flex; + flex-wrap: wrap; + align-items: center; +} + +.item-content .description h3 { + width: 100%; +} + .showCards .fab-item .item-content { margin-left: 100px; min-height: 60px; @@ -371,10 +396,10 @@ md-slider.md-default-theme.hue .md-thumb:after { width: 56px; height: 56px; background-color: #CCC; - position: absolute; text-align: center; font-size: 30px; line-height: 54px; + float: left; } .fab-item .rectangle { @@ -398,10 +423,6 @@ md-slider.md-default-theme.hue .md-thumb:after { font-size: 15px; } -.fab-item hr.border-line { - margin: 10px 0px; -} - /* Input */ md-input-group.md-default-theme input, md-input-group.md-default-theme textarea { @@ -667,10 +688,6 @@ md-progress-circular.md-default-theme .md-inner .md-left .md-half-circle margin-top: 5px; } -.channel .circle { - margin-left: 8px; -} - .channels .channelGroup { margin-top: 20px; } @@ -985,14 +1002,6 @@ md-chips strong { outline: 0; } -.thing .cbody { - margin-bottom: 10px; -} - -.fab-item hr.border-line { - margin: 0px; -} - .material-icons.color-remove { font-size: 12px; margin-top: 10px; @@ -1051,6 +1060,10 @@ md-dialog-content .dayParameter { min-height: 48px; } +.item-container { + padding-left: 0px; +} + .container.itemConfig .md-select-value, .container .includeConfig .md-select-value { padding: 2px 2px 1px 2px; } @@ -1145,16 +1158,18 @@ background-color: green; .rule .actions i,.item-content .actions i{ margin:1px; } - .container { - padding-left:8px; - padding-right:4px; - } } .item-content p, .item-content h3 { word-wrap: normal; text-overflow:ellipsis; overflow:hidden; + margin-top: 0px; +} + +.item-content p { + margin: 0; + width: 100%; } .item-content h3:focus { @@ -1170,13 +1185,12 @@ background-color: green; margin-right: 20px; } -.linkedItems { - -/* background-color:#e6e6e6 */ +.linkedItems.item-content { + margin-left: 56px; + display: block; } .linkedItems>div{ - border-bottom: 1px solid #ddd; margin: 5px; } @@ -1187,12 +1201,12 @@ background-color: green; font-size:15px; } -.fab-item .linkedItems button.md-fab { - position:relative; +.linkedItems button.md-fab { + float: none; + margin: 0 0 4px 6px; } .linkedItems>.row { - padding-bottom: 10px; margin-top: 10px; } @@ -1416,3 +1430,48 @@ md-progress-circular.firmware { -webkit-transition: none !important; transition: none !important; } + +.bindings.white-bg { + padding-top: 30px; +} + +.bindings.white-bg.has-section-header { + padding-top: 80px; +} + +.binding.white-bg { + padding-top: 30px; +} + +.binding .item-content .actions { + width: 60px; +} + +.binding.white-bg.has-section-header { + padding-top: 80px; +} + +.fab-item hr.border-line { + margin: 10px 0 0 76px; +} + +.binding :not(md-expansion-panel) .md-title { + padding-bottom: 10px; + flex-grow: 2 +} +.binding .things .search { + padding: 0 0 10px 0; +} + +.binding .things md-expansion-panel .md-title.ng-binding { + padding-bottom: 0; +} + +search-field { + display: flex; + width: 100%; +} + +search-field i { + margin-top: 4px; +} diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/layout.css b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/layout.css index 902e9101bb2..247feeca3c3 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/layout.css +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/layout.css @@ -390,8 +390,7 @@ md-tabs.section-tabs { .section-header .toolbar { position: absolute; - bottom: -50px; - left: 15px; + left: 7px; z-index: 8; } diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/views.css b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/views.css index cf8352abdf8..e7eb827be85 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/views.css +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/css/views.css @@ -136,9 +136,9 @@ section#main.header-tabs { /* Inbox */ .scan .fab-item md-progress-circular { - position: absolute; - left: 0px; - right: 0px; + float: left; + margin: 0px; + width: 56px !important; } .thing-type-choose { diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/index.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/index.html index 3bbdd3e888a..e2eda35ede1 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/index.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/index.html @@ -12,6 +12,7 @@ + diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/app.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/app.js index 00d176f70fe..e4631444f80 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/app.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/app.js @@ -1,4 +1,4 @@ -angular.module('PaperUI', [ 'PaperUI.controllers', 'PaperUI.controllers.control', 'PaperUI.controllers.setup', 'PaperUI.controllers.configuration', 'PaperUI.controllers.extension', 'PaperUI.controllers.rules', 'PaperUI.services', 'PaperUI.services.rest', 'PaperUI.extensions', 'ngRoute', 'ngResource', 'ngMaterial', 'ngMessages', 'ngSanitize', 'ui.sortable' ]).config([ '$routeProvider', '$httpProvider', 'globalConfig', '$mdDateLocaleProvider', 'moduleConfig', 'dateTimeProvider', function($routeProvider, httpProvider, globalConfig, $mdDateLocaleProvider, moduleConfig, dateTimeProvider) { +angular.module('PaperUI', [ 'PaperUI.controllers', 'PaperUI.controllers.control', 'PaperUI.controllers.setup', 'PaperUI.controllers.configuration', 'PaperUI.controllers.configuration.bindings', 'PaperUI.controllers.extension', 'PaperUI.controllers.rules', 'PaperUI.services', 'PaperUI.services.rest', 'PaperUI.services.repositories', 'PaperUI.extensions', 'ngRoute', 'ngResource', 'ngMaterial', 'ngMessages', 'ngSanitize', 'ui.sortable', 'material.components.expansionPanels' ]).config([ '$routeProvider', '$httpProvider', 'globalConfig', '$mdDateLocaleProvider', 'moduleConfig', 'dateTimeProvider', function($routeProvider, httpProvider, globalConfig, $mdDateLocaleProvider, moduleConfig, dateTimeProvider) { $routeProvider.when('/control', { templateUrl : 'partials/control.html', controller : 'ControlPageController', @@ -32,10 +32,6 @@ angular.module('PaperUI', [ 'PaperUI.controllers', 'PaperUI.controllers.control' title : 'Inbox' }).when('/configuration', { redirectTo : '/configuration/bindings' - }).when('/configuration/bindings', { - templateUrl : 'partials/configuration.html', - controller : 'ConfigurationPageController', - title : 'Configuration' }).when('/configuration/services', { templateUrl : 'partials/configuration.html', controller : 'ConfigurationPageController', @@ -366,4 +362,4 @@ angular.module('PaperUI', [ 'PaperUI.controllers', 'PaperUI.controllers.control' $rootScope.advancedMode = advancedMode === 'true'; } -} ]); \ No newline at end of file +} ]); diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.bindings.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.bindings.js new file mode 100644 index 00000000000..993dced22d8 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.bindings.js @@ -0,0 +1,184 @@ +angular.module('PaperUI.controllers.configuration.bindings', [ 'ngRoute', 'PaperUI.directive.searchField' ]).config([ '$routeProvider', function($routeProvider) { + // Configure the routes for this controller + $routeProvider.when('/configuration/bindings', { + templateUrl : 'partials/configuration.bindings.html', + controller : 'BindingController', + title : 'Configuration' + }).when('/configuration/bindings/:bindingId', { + templateUrl : 'partials/configuration.binding.html', + controller : 'BindingDetailController', + title : 'Configuration' + }); +} ]).controller('BindingController', function($scope, $location, $mdDialog, bindingRepository, extensionService) { + /** + * This is the main binding controller to display all bindings + */ + $scope.navigateTo = function(path) { + $location.path(path); + } + + $scope.bindings = []; + $scope.extensionServiceAvailable = false; + + extensionService.isAvailable(function(available) { + $scope.extensionServiceAvailable = available; + }) + + $scope.setSubtitle([ 'Bindings' ]); + $scope.setHeaderText('Shows all installed bindings.'); + $scope.refresh = function() { + bindingRepository.getAll(function(bindings) { + $scope.bindings = bindings; + }, true); + }; + + $scope.configure = function(binding, event) { + event.stopPropagation(); + $mdDialog.show({ + controller : 'ConfigureBindingDialogController', + templateUrl : 'partials/dialog.configurebinding.html', + targetEvent : event, + hasBackdrop : true, + locals : { + binding : binding + } + }); + } + + $scope.isConfigurable = function(binding) { + return binding.configDescriptionURI ? true : false; + } + + $scope.refresh(); + +}).controller('BindingDetailController', function($scope, $location, $mdExpansionPanel, $mdDialog, thingTypeRepository, bindingRepository, extensionService) { + var bindingId = $scope.path[3]; + $scope.filter = { + text : '' + }; + + $scope.navigateTo = function(path) { + $location.path(path); + } + + $scope.filterItems = function(lookupFields, searchText) { + return function(item) { + if (searchText && searchText.length > 0) { + for (var i = 0; i < lookupFields.length; i++) { + if (item[lookupFields[i]] && item[lookupFields[i]].toUpperCase().indexOf(searchText.toUpperCase()) != -1) { + return true; + } + } + return false + } + return true; + } + } + + $scope.clearFilter = function(event) { + if (!event || event.keyCode === 27) { + $scope.filter.text = ''; + } + } + + $scope.binding = undefined; + bindingRepository.getOne(function(binding) { + return binding.id === bindingId; + }, function(binding) { + $scope.setSubtitle([ 'Bindings', binding.name ]); + $scope.setHeaderText('Shows detailed binding information.'); + + $scope.binding = binding; + $scope.binding.thingTypes = []; + thingTypeRepository.getAll(function(thingTypes) { + angular.forEach(thingTypes, function(thingType) { + if (thingType.UID.split(':')[0] === binding.id) { + $scope.binding.thingTypes.push(thingType); + } + }); + }); + }); + + $scope.configure = function(event) { + $mdDialog.show({ + controller : 'ConfigureBindingDialogController', + templateUrl : 'partials/dialog.configurebinding.html', + targetEvent : event, + hasBackdrop : true, + locals : { + binding : $scope.binding + } + }); + } + + $scope.isConfigurable = function() { + return $scope.binding.configDescriptionURI ? true : false; + } +}).controller('ConfigureBindingDialogController', function($scope, $mdDialog, bindingRepository, bindingService, configService, configDescriptionService, toastService, binding) { + + $scope.binding = binding; + $scope.parameters = []; + $scope.config = {}; + + if (binding.configDescriptionURI) { + $scope.expertMode = false; + configDescriptionService.getByUri({ + uri : binding.configDescriptionURI + }, function(configDescription) { + if (configDescription) { + $scope.parameters = configService.getRenderingModel(configDescription.parameters, configDescription.parameterGroups); + $scope.configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters); + $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); + } + }); + } + if (binding) { + bindingService.getConfigById({ + id : binding.id + }).$promise.then(function(config) { + $scope.configuration = configService.convertValues(config); + $scope.configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters); + $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); + + }, function(failed) { + $scope.configuration = {}; + $scope.configArray = configService.getConfigAsArray($scope.configuration); + }); + } else { + $scope.newConfig = true; + $scope.serviceId = ''; + $scope.configuration = { + '' : '' + }; + $scope.configArray = []; + $scope.expertMode = true; + } + $scope.close = function() { + $mdDialog.hide(); + } + $scope.addParameter = function() { + $scope.configArray.push({ + name : '', + value : undefined + }); + } + $scope.save = function() { + if ($scope.expertMode) { + $scope.configuration = configService.getConfigAsObject($scope.configArray, $scope.parameters); + } + var configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters, true); + bindingService.updateConfig({ + id : binding.id + }, configuration, function() { + $mdDialog.hide(); + toastService.showDefaultToast('Binding config updated.'); + }); + } + $scope.$watch('expertMode', function() { + if ($scope.expertMode) { + $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); + } else { + $scope.configuration = configService.getConfigAsObject($scope.configArray, $scope.parameters); + } + }); +}); \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.js index 33bc7453a25..5d1b29a0613 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.configuration.js @@ -23,126 +23,6 @@ angular.module('PaperUI.controllers.configuration', [ 'PaperUI.constants', 'Pape } }; getThingTypes(); -}).controller('BindingController', function($scope, $mdDialog, bindingRepository) { - $scope.setSubtitle([ 'Bindings' ]); - $scope.setHeaderText('Shows all installed bindings.'); - $scope.refresh = function() { - bindingRepository.getAll(true); - }; - $scope.openBindingInfoDialog = function(bindingId, event) { - $mdDialog.show({ - controller : 'BindingInfoDialogController', - templateUrl : 'partials/dialog.bindinginfo.html', - targetEvent : event, - hasBackdrop : true, - locals : { - bindingId : bindingId - } - }); - } - $scope.configure = function(bindingId, configDescriptionURI, event) { - $mdDialog.show({ - controller : 'ConfigureBindingDialogController', - templateUrl : 'partials/dialog.configurebinding.html', - targetEvent : event, - hasBackdrop : true, - locals : { - bindingId : bindingId, - configDescriptionURI : configDescriptionURI - } - }); - } - bindingRepository.getAll(); -}).controller('BindingInfoDialogController', function($scope, $mdDialog, thingTypeRepository, bindingRepository, bindingId) { - $scope.binding = undefined; - bindingRepository.getOne(function(binding) { - return binding.id === bindingId; - }, function(binding) { - $scope.binding = binding; - $scope.binding.thingTypes = []; - thingTypeRepository.getAll(function(thingTypes) { - $.each(thingTypes, function(index, thingType) { - if (thingType.UID.split(':')[0] === binding.id) { - $scope.binding.thingTypes.push(thingType); - } - }); - }); - }); - $scope.close = function() { - $mdDialog.hide(); - } -}).controller('ConfigureBindingDialogController', function($scope, $mdDialog, bindingRepository, bindingService, configService, configDescriptionService, toastService, bindingId, configDescriptionURI) { - - $scope.binding = null; - $scope.parameters = []; - $scope.config = {}; - - if (configDescriptionURI) { - $scope.expertMode = false; - configDescriptionService.getByUri({ - uri : configDescriptionURI - }, function(configDescription) { - if (configDescription) { - $scope.parameters = configService.getRenderingModel(configDescription.parameters, configDescription.parameterGroups); - $scope.configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters); - $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); - } - }); - } - if (bindingId) { - bindingRepository.getOne(function(binding) { - return binding.id === bindingId; - }, function(binding) { - $scope.binding = binding; - }); - bindingService.getConfigById({ - id : bindingId - }).$promise.then(function(config) { - $scope.configuration = configService.convertValues(config); - $scope.configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters); - $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); - - }, function(failed) { - $scope.configuration = {}; - $scope.configArray = configService.getConfigAsArray($scope.configuration); - }); - } else { - $scope.newConfig = true; - $scope.serviceId = ''; - $scope.configuration = { - '' : '' - }; - $scope.configArray = []; - $scope.expertMode = true; - } - $scope.close = function() { - $mdDialog.hide(); - } - $scope.addParameter = function() { - $scope.configArray.push({ - name : '', - value : undefined - }); - } - $scope.save = function() { - if ($scope.expertMode) { - $scope.configuration = configService.getConfigAsObject($scope.configArray, $scope.parameters); - } - var configuration = configService.setConfigDefaults($scope.configuration, $scope.parameters, true); - bindingService.updateConfig({ - id : bindingId - }, configuration, function() { - $mdDialog.hide(); - toastService.showDefaultToast('Binding config updated.'); - }); - } - $scope.$watch('expertMode', function() { - if ($scope.expertMode) { - $scope.configArray = configService.getConfigAsArray($scope.configuration, $scope.parameters); - } else { - $scope.configuration = configService.getConfigAsObject($scope.configArray, $scope.parameters); - } - }); }).controller('ServicesController', function($scope, $mdDialog, serviceConfigService, toastService) { $scope.setSubtitle([ 'Services' ]); $scope.setHeaderText('Shows all configurable services.'); diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.control.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.control.js index 438e6c15275..a0fbc120f8d 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.control.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/controllers.control.js @@ -1,197 +1,158 @@ angular.module('PaperUI.controllers.control', []) // -.controller('ControlPageController', function($scope, $routeParams, $location, $timeout, itemRepository, thingTypeRepository, thingService, thingTypeService, channelTypeService, thingConfigService, imageService, util) { - $scope.items = []; - $scope.selectedIndex = 0; +.controller('ControlPageController', function($scope, $routeParams, $location, $timeout, $filter, itemRepository, thingTypeRepository, util, thingRepository, channelTypeRepository) { $scope.tabs = []; - $scope.things = []; - var thingTypes = []; $scope.navigateTo = function(path) { $location.path(path); } - $scope.next = function() { - var newIndex = $scope.selectedIndex + 1; - if (newIndex > ($scope.tabs.length - 1)) { - newIndex = 0; - } - $scope.selectedIndex = newIndex; - } - $scope.prev = function() { - var newIndex = $scope.selectedIndex - 1; - if (newIndex < 0) { - newIndex = $scope.tabs.length - 1; - } - $scope.selectedIndex = newIndex; - } $scope.refresh = function() { - itemRepository.getAll(function(items) { - $scope.items = items; + itemRepository.getAll(function() { + channelTypeRepository.getAll(function() { + thingTypeRepository.getAll(function() { + renderTabs(); + }, true) + }, true) }, true); - getThings(); - } - $scope.channelTypes = []; - $scope.thingTypes = []; - $scope.thingChannels = []; - $scope.isLoadComplete = false; - var thingList; - function getThings() { - $scope.things = []; - thingService.getAll().$promise.then(function(things) { - thingList = things; - $scope.isLoadComplete = false; - thingTypeService.getAll().$promise.then(function(thingTypes) { - $scope.thingTypes = thingTypes; - channelTypeService.getAll().$promise.then(function(channels) { - $scope.channelTypes = channels; - var thingTypesUsed = getUsedThingTypesUIDs(thingList); - for (var i = 0; i < thingTypesUsed.length; i++) { - thingTypeService.getByUid({ - thingTypeUID : thingTypesUsed[i] - }, function(thingType) { - thingTypes.push(thingType); - for (var t_i = 0; t_i < thingList.length; t_i++) { - if (thingList[t_i].thingTypeUID === thingType.UID) { - renderThing(thingList[t_i], thingType, $scope.channelTypes); - } - if (i == thingTypesUsed.length && t_i == thingList.length - 1) { - getTabs(); - } - } - }); - } - }); - }); - - }); - } - function renderThing(thing, thingType, channelTypes) { - thing.thingChannels = thingConfigService.getThingChannels(thing, thingType, channelTypes, true); - angular.forEach(thing.thingChannels, function(value, key) { - thing.thingChannels[key].channels = $.grep(thing.thingChannels[key].channels, function(channel, i) { - return channel.linkedItems.length > 0; - }); - }); - if (thingHasChannels(thing) && $scope.things.indexOf(thing) == -1) { - $scope.things.push(thing); - } } - function thingHasChannels(thing) { - for (var i = 0; i < thing.thingChannels.length; i++) { - if (thing.thingChannels[i].channels && thing.thingChannels[i].channels.length > 0) { - return true; - } - } - return false; + + function renderTabs() { + thingRepository.getAll(function(things) { + $scope.tabs = getTabs(things); + }) } - function getTabs() { - if (!$scope.things) { - return; + function getTabs(things) { + if (!things) { + return []; } - var arr = [], otherTab = false; - for (var i = 0; i < $scope.things.length; i++) { - if ($scope.things[i].location && $scope.things[i].location.toUpperCase() != "OTHER") { - $scope.things[i].location = $scope.things[i].location.toUpperCase(); - arr[$scope.things[i].location] = $scope.things[i].location; - } else { - $scope.things[i].location = "OTHER"; - otherTab = true; + + var locations = new Set(); + angular.forEach(things, function(thing) { + var location = thing.location ? thing.location.toUpperCase() : 'OTHER' + thing.location = location + locations.add(location) + }) + + var renderedTabs = Array.from(locations) + renderedTabs = renderedTabs.sort(function(a, b) { + if (a === 'OTHER') { + return 1; } - } - for ( var value in arr) { - if (!hasTab(value)) { - $scope.tabs.push({ - name : value - }); + if (b === 'OTHER') { + return -1; } - } - if (otherTab && !hasTab("OTHER")) { - $scope.tabs.push({ - name : "OTHER" - }); - } - $scope.isLoadComplete = true; - } - function hasTab(name) { - return $.grep($scope.tabs, function(tab) { - return tab.name == name; - }).length > 0; - } + return a < b ? -1 : a > b ? 1 : 0 + }) - function getThingTypeLocal(thingTypeUID) { - var thingTypeComplete = $.grep(thingTypes, function(thingType) { - return thingType.UID == thingTypeUID; + return renderedTabs.map(function(location) { + return { + name : location, + thingCount : 1 + } }); - return thingTypeComplete.length > 0 ? thingTypeComplete : null; } - function getUsedThingTypesUIDs(things) { - var thingTypeUIDs = []; - if (things) { - for (var i = 0; i < things.length; i++) { - if (thingTypeUIDs.indexOf(things[i].thingTypeUID) == -1) { - thingTypeUIDs.push(things[i].thingTypeUID); - } - } - } - return thingTypeUIDs; - } + $scope.refresh(); + +}).controller('ControlController', function($scope, $timeout, $filter, itemService, util, $attrs, thingRepository, channelTypeRepository, thingTypeRepository, thingConfigService, imageService) { + + var tabName = $scope.$parent.tab.name + var tabIndex = $scope.$parent.$index - $scope.tabComparator = function(actual, expected) { - return actual == expected; + $scope.things = []; + var renderedThings = [] + + var renderItems = function() { + var redraw; + thingRepository.getAll(function(things) { + var thingsForTab = things.filter(function(thing) { + var thingLocation = thing.location ? thing.location.toUpperCase() : 'OTHER' + return thingLocation === tabName; + }) + channelTypeRepository.getAll(function(channelTypes) { + angular.forEach(thingsForTab, function(thing) { + thingTypeRepository.getOne(function(thingType) { + return thingType.UID === thing.thingTypeUID + }, function(thingType) { + var renderedThing = renderThing(thing, thingType, channelTypes); + if (renderedThing) { + renderedThings.push(renderedThing); + renderedThings = renderedThings.sort(function(a, b) { + return a.label < b.label ? -1 : a.label > b.label ? 1 : 0 + }) + $timeout.cancel(redraw) + redraw = $timeout(function() { + $scope.things = renderedThings; + masonry() + }, 0, true) + } + }, false) + }) + }, false) + }, false) + } + + var masonry = function() { + $timeout(function() { + new Masonry('#items-' + tabIndex, {}); + }, 100, false); } - $scope.getItems = function(itemNames) { - var items = []; - angular.forEach(itemNames, function(itemName) { - items.push($scope.getItem(itemName)); + function renderThing(thing, thingType, channelTypes) { + thing.thingChannels = thingConfigService.getThingChannels(thing, thingType, channelTypes, true); + angular.forEach(thing.thingChannels, function(thingChannel) { + thingChannel.channels = thingChannel.channels.filter(function(channel) { + return channel.linkedItems.length > 0; + }); }); - return items; + var hasChannels = false; + angular.forEach(thing.thingChannels, function(channelGroup) { + angular.forEach(channelGroup.channels, function(channel) { + channel.items = getItems(channel.linkedItems) + hasChannels = true; + }) + }) + + if (hasChannels) { + return thing; + } } - $scope.getItem = function(itemName) { - for (var i = 0; i < $scope.data.items.length; i++) { - var item = $scope.data.items[i]; - if (item.name === itemName) { - if (item.type && (item.type == "Number" || item.groupType == "Number" || item.type == "Rollershutter")) { - var parsedValue = Number(item.state); - if (isNaN(parsedValue)) { - item.state = null; - } else { - item.state = parsedValue; - } - } - if (item.type && item.type == "Image") { - imageService.getItemState(item.name).then(function(state) { - item.state = state; - item.imageLoaded = true; - }); - item.imageLoaded = false; + var getItems = function(itemNames) { + var items = $scope.data.items.filter(function(item) { + return itemNames.indexOf(item.name) >= 0 + }) + angular.forEach(items, function(item) { + + if (item.type && (item.type == "Number" || item.groupType == "Number" || item.type == "Rollershutter")) { + var parsedValue = Number(item.state); + if (isNaN(parsedValue)) { + item.state = null; + } else { + item.state = parsedValue; } - item.stateText = util.getItemStateText(item); - return item; } - } - return null; - } + if (item.type && item.type == "Image") { + imageService.getItemState(item.name).then(function(state) { + item.state = state; + item.imageLoaded = true; + }); + item.imageLoaded = false; + } + item.stateText = util.getItemStateText(item); - $scope.masonry = function() { - if ($scope.data.items) { - $timeout(function() { - var itemContainer = '#items-' + $scope.selectedIndex; - new Masonry(itemContainer, {}); - }, 100, true); - } + item.readOnly = isReadOnly(item); + }) + + return items; } - $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { - $scope.masonry(); - }); - $scope.refresh(); -}).controller('ControlController', function($scope, $timeout, $filter, itemService, util) { + var isReadOnly = function(item) { + return item.stateDescription ? item.stateDescription.readOnly : false; + } $scope.getItemName = function(itemName) { return itemName.replace(/_/g, ' '); @@ -267,39 +228,31 @@ angular.module('PaperUI.controllers.control', []) // 'Zoom' : {}, } - $scope.getLabel = function(itemCategory, name, defaultLabel) { - if (name) { - var item = $.grep($scope.items, function(item) { - return item.name == name; - }); - if (item.length > 0) { - return item[0].label; - } + $scope.getLabel = function(item, defaultLabel) { + if (item.name) { + return item.label; } - if (itemCategory) { - var category = categories[itemCategory]; + if (item.category) { + var category = categories[item.category]; if (category) { - return category.label ? category.label : itemCategory; + return category.label ? category.label : item.category; } else { return defaultLabel; } - } else { - return defaultLabel; } + + return defaultLabel; } + $scope.getIcon = function(itemCategory, fallbackIcon) { var defaultIcon = fallbackIcon ? fallbackIcon : 'radio_button_unchecked'; - if (itemCategory) { - var category = categories[itemCategory]; - if (category) { - return category.icon ? category.icon : defaultIcon; - } else { - return defaultIcon; - } - } else { - return defaultIcon; + + if (itemCategory && categories[itemCategory] && categories[itemCategory].icon) { + return categories[itemCategory].icon } + + return defaultIcon; } $scope.showSwitch = function(itemCategory) { if (itemCategory) { @@ -310,9 +263,6 @@ angular.module('PaperUI.controllers.control', []) // } return false; } - $scope.isReadOnly = function(item) { - return item.stateDescription ? item.stateDescription.readOnly : false; - } /** * Check if the item has a configured option list. Returns true if there are options, otherwise false. @@ -323,6 +273,9 @@ angular.module('PaperUI.controllers.control', []) // $scope.isOptionList = function(item) { return (item.stateDescription != null && item.stateDescription.options.length > 0) } + + renderItems() + }).controller('ItemController', function($rootScope, $scope, itemService, util) { $scope.editMode = false; $scope.sendCommand = function(command, updateState) { diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/directives/directive.searchField.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/directives/directive.searchField.js new file mode 100644 index 00000000000..5130be0b562 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/directives/directive.searchField.js @@ -0,0 +1,19 @@ +angular.module('PaperUI.directive.searchField', []) // +.directive('searchField', function() { + return { + restrict : 'E', + scope : { + model : '=', + placeholder : '=?' + }, + templateUrl : 'partials/directive.searchField.html', + link : function(scope, element, attrs, controllers) { + scope.placeholder = attrs.placeholder ? attrs.placeholder : 'Search' + scope.clearSearchField = function(event) { + if (!event || event.keyCode === 27) { + scope.model = ''; + } + } + } + } +}); \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.js index 67c1312865a..39e39184de8 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.js @@ -192,13 +192,20 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c insertEmptyOption(parameter); } - if (type === 'INTEGER' || type === 'DECIMAL') { - angular.forEach(parameter.options, function(option) { - option.value = parseInt(option.value); - }) - if (parameter.defaultValue) { - parameter.defaultValue = parseInt(parameter.defaultValue); - } + if (type === 'INTEGER') { + adjustNumberValue(parameter, parseInt); + } + if (type === 'DECIMAL') { + adjustNumberValue(parameter, parseFloat); + } + } + + var adjustNumberValue = function(parameter, parseNumberFunction) { + angular.forEach(parameter.options, function(option) { + option.value = parseNumberFunction(option.value); + }) + if (parameter.defaultValue) { + parameter.defaultValue = parseNumberFunction(parameter.defaultValue); } } @@ -428,8 +435,10 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c if (String(value).length > 0) { thing.configuration[parameter.name] = String(value).toUpperCase() == "TRUE"; } - } else if (parameter.type === 'INTEGER' || parameter.type === 'DECIMAL') { + } else if (parameter.type === 'INTEGER') { thing.configuration[parameter.name] = parameter.defaultValue != null && parameter.defaultValue !== "" ? parseInt(parameter.defaultValue) : ""; + } else if (parameter.type === 'DECIMAL') { + thing.configuration[parameter.name] = parameter.defaultValue != null && parameter.defaultValue !== "" ? parseFloat(parameter.defaultValue) : ""; } else { thing.configuration[parameter.name] = parameter.defaultValue; } @@ -480,8 +489,10 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c if (String(value).length > 0) { configuration[parameter.name] = String(value).toUpperCase() == "TRUE"; } - } else if (!hasValue && (parameter.type === 'INTEGER' || parameter.type === 'DECIMAL')) { + } else if (!hasValue && parameter.type === 'INTEGER') { configuration[parameter.name] = parameter.defaultValue != null && parameter.defaultValue !== "" ? parseInt(parameter.defaultValue) : null; + } else if (!hasValue && parameter.type === 'DECIMAL') { + configuration[parameter.name] = parameter.defaultValue != null && parameter.defaultValue !== "" ? parseFloat(parameter.defaultValue) : null; } else if (!hasValue) { configuration[parameter.name] = parameter.defaultValue; } @@ -524,25 +535,26 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c var thingChannels = []; var includedChannels = []; if (thingType && thingType.channelGroups && thingType.channelGroups.length > 0) { - for (var i = 0; i < thingType.channelGroups.length; i++) { + angular.forEach(thingType.channelGroups, function(channelGroup) { var group = {}; - group.name = thingType.channelGroups[i].label; - group.description = thingType.channelGroups[i].description; - group.channels = this.matchGroup(thing.channels, thingType.channelGroups[i].id); + group.name = channelGroup.label; + group.description = channelGroup.description; + group.channels = this.matchGroup(thing.channels, channelGroup.id); includedChannels = includedChannels.concat(group.channels); group.channels = advanced ? group.channels : this.filterAdvance(thingType, channelTypes, group.channels, false); thingChannels.push(group); - } + }, this) + var group = { "name" : "Others", "description" : "Other channels", "channels" : [] }; - for (var i = 0; i < thing.channels.length; i++) { - if (includedChannels.indexOf(thing.channels[i]) == -1) { - group.channels.push(thing.channels[i]); + angular.forEach(thing.channels, function(channel) { + if (includedChannels.indexOf(channel) == -1) { + group.channels.push(channel); } - } + }) if (group.channels && group.channels.length > 0) { thingChannels.push(group); } @@ -557,62 +569,43 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c }, filterAdvance : function(thingType, channelTypes, channels, advanced) { - var self = this; - self.thingType = thingType, self.channelTypes = channelTypes, self.channels = channels; - return $.grep(channels, function(channel, i) { - var channelType = self.getChannelTypeByUID(self.thingType, self.channelTypes, channel.channelTypeUID); + return channels.filter(function(channel) { + var channelType = this.getChannelTypeByUID(thingType, channelTypes, channel.channelTypeUID); return channelType ? advanced == channelType.advanced : true; - }); + }, this); }, getChannelTypeByUID : function(thingType, channelTypes, channelUID) { if (thingType) { if (thingType.channels && thingType.channels.length > 0) { - var c, c_i, c_l; - for (c_i = 0, c_l = thingType.channels.length; c_i < c_l; ++c_i) { - c = thingType.channels[c_i]; - if (c.typeUID == channelUID) { - return c; - } + var result = thingType.channels.filter(function(channel) { + return channel.typeUID === channelUID; + }) + if (result.length > 0) { + return result[0]; } } if (thingType.channelGroups && thingType.channelGroups.length > 0) { - var c, c_i, c_l; - var cg, cg_i, cg_l; - for (cg_i = 0, cg_l = thingType.channelGroups.length; cg_i < cg_l; ++cg_i) { - cg = thingType.channelGroups[cg_i]; - if (cg && cg.channels) { - for (c_i = 0, c_l = cg.channels.length; c_i < c_l; ++c_i) { - c = cg.channels[c_i]; - if (c.typeUID == channelUID) { - return c; - } + angular.forEach(thingType.channelGroups, function(channelGroup) { + if (channelGroup && channelGroup.channels) { + var result = channelGroup.channels.filter(function(channel) { + return channel.typeUID === channelUID; + }) + if (result.length > 0) { + return result[0]; } } - } + }) } } if (channelTypes) { - var c = {}, c_i, c_l; - for (c_i = 0, c_l = channelTypes.length; c_i < c_l; ++c_i) { - c = channelTypes[c_i]; - if (c.UID == channelUID) { - return c; - } - } + return this.getChannelFromChannelTypes(channelTypes, channelUID); } - return; }, getChannelFromChannelTypes : function(channelTypes, channelUID) { - if (channelTypes) { - var c = {}, c_i, c_l; - for (c_i = 0, c_l = channelTypes.length; c_i < c_l; ++c_i) { - c = channelTypes[c_i]; - if (c.UID == channelUID) { - return c; - } - } - } - return; + var result = channelTypes.filter(function(channelType) { + return channelType.UID === channelUID; + }) + return result.length > 0 ? result[0] : null; }, matchGroup : function(arr, id) { var matched = []; @@ -627,11 +620,11 @@ angular.module('PaperUI.services', [ 'PaperUI.services.repositories', 'PaperUI.c return matched; }, addTypeToChannels : function(groups, channelTypes) { - for (var g_i = 0; g_i < groups.length; g_i++) { - for (var c_i = 0; c_i < groups[g_i].channels.length; c_i++) { - groups[g_i].channels[c_i].channelType = this.getChannelFromChannelTypes(channelTypes, groups[g_i].channels[c_i].channelTypeUID); - } - } + angular.forEach(groups, function(group) { + angular.forEach(group.channels, function(channel) { + channel.channelType = this.getChannelFromChannelTypes(channelTypes, channel.channelTypeUID); + }, this) + }, this) return groups; } } diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.repositories.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.repositories.js index 17c4468d0b8..889d6495cd7 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.repositories.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.repositories.js @@ -1,12 +1,15 @@ -var Repository = function($q, $rootScope, remoteService, dataType, staticData) { +var Repository = function($q, $rootScope, remoteService, dataType, staticData, getOneFunction, idParameterName, elmentId) { var self = this; - var cacheEnabled = true; - var dirty = false; - var initialFetch = false; + this.cacheEnabled = true; + this.dirty = false; + this.initialFetch = false; + this.staticData = staticData this.setDirty = function() { - this.dirty = true; + self.dirty = true; } + + this.singleElements = getOneFunction ? {} : null; this.getAll = function(callback, refresh) { if (typeof callback === 'boolean') { refresh = true; @@ -28,11 +31,11 @@ var Repository = function($q, $rootScope, remoteService, dataType, staticData) { return; } }); - if (cacheEnabled && staticData && self.initialFetch && !refresh && !self.dirty) { + if (self.cacheEnabled && self.staticData && self.initialFetch && !refresh && !self.dirty) { deferred.resolve($rootScope.data[dataType]); } else { remoteService.getAll(function(data) { - if ((!cacheEnabled || (data.length != $rootScope.data[dataType].length) || self.dirty || refresh)) { + if ((!self.cacheEnabled || (data.length != $rootScope.data[dataType].length) || self.dirty || refresh)) { self.initialFetch = true; $rootScope.data[dataType] = data; self.dirty = false; @@ -47,7 +50,7 @@ var Repository = function($q, $rootScope, remoteService, dataType, staticData) { deferred.resolve('No update'); } }); - if (cacheEnabled && self.initialFetch) { + if (self.cacheEnabled && self.initialFetch) { deferred.notify($rootScope.data[dataType]); } } @@ -56,11 +59,11 @@ var Repository = function($q, $rootScope, remoteService, dataType, staticData) { this.getOne = function(condition, callback, refresh) { var element = self.find(condition); if (element != null && !this.dirty && !refresh) { - callback(element); + self.resolveSingleElement(callback, element) } else { self.getAll(null, true).then(function(res) { if (callback) { - callback(self.find(condition)); + self.resolveSingleElement(callback, self.find(condition)); return; } else { return; @@ -73,6 +76,22 @@ var Repository = function($q, $rootScope, remoteService, dataType, staticData) { }); } }; + + this.resolveSingleElement = function(callback, element) { + if (getOneFunction && self.singleElements[element.UID]) { + callback(self.singleElements[element.UID]); + } else if (getOneFunction) { + var parameter = {}; + parameter[idParameterName] = element[elmentId]; + getOneFunction(parameter, function(singleElement) { + self.singleElements[element.UID] = singleElement; + callback(singleElement) + }) + } else { + callback(element); + } + } + this.find = function(condition) { for (var i = 0; i < $rootScope.data[dataType].length; i++) { var element = $rootScope.data[dataType][i]; @@ -112,7 +131,10 @@ angular.module('PaperUI.services.repositories', []).factory('bindingRepository', return new Repository($q, $rootScope, bindingService, 'bindings', true); }).factory('thingTypeRepository', function($q, $rootScope, thingTypeService) { $rootScope.data.thingTypes = []; - return new Repository($q, $rootScope, thingTypeService, 'thingTypes', true); + return new Repository($q, $rootScope, thingTypeService, 'thingTypes', true, thingTypeService.getByUid, 'thingTypeUID', 'UID'); +}).factory('channelTypeRepository', function($q, $rootScope, channelTypeService) { + $rootScope.data.channelTypes = []; + return new Repository($q, $rootScope, channelTypeService, 'channelTypes', true); }).factory('discoveryResultRepository', function($q, $rootScope, inboxService, eventService) { var repository = new Repository($q, $rootScope, inboxService, 'discoveryResults') $rootScope.data.discoveryResults = []; diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.rest.js b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.rest.js index 5b352986585..c3f437b9053 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.rest.js +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/js/services.rest.js @@ -400,8 +400,8 @@ angular.module('PaperUI.services.rest', [ 'PaperUI.constants', 'ngResource' ]).c url : restConfig.restPath + '/config-descriptions/:uri' }, }); -}).factory('extensionService', function($resource, restConfig) { - return $resource(restConfig.restPath + '/extensions', {}, { +}).factory('extensionService', function($resource, restConfig, $http) { + var extensionService = $resource(restConfig.restPath + '/extensions', {}, { getAll : { method : 'GET', isArray : true, @@ -448,6 +448,16 @@ angular.module('PaperUI.services.rest', [ 'PaperUI.constants', 'ngResource' ]).c url : restConfig.restPath + '/extensions/:id/uninstall' } }); + + extensionService.isAvailable = function(callback) { + $http.head(restConfig.restPath + '/extensions').then(function() { + callback(true); + }, function() { + callback(false); + }); + } + + return extensionService; }).factory('ruleService', function($resource, restConfig) { return $resource(restConfig.restPath + '/rules', {}, { getAll : { diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.configuration.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.configuration.html new file mode 100644 index 00000000000..4b924c71a39 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.configuration.html @@ -0,0 +1,19 @@ +

    + Configure {{binding.name}} {{binding.id}} +

    +
    +
    +
    +
    + +
    + + \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.html new file mode 100644 index 00000000000..95381a3014b --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.html @@ -0,0 +1,19 @@ +
    +
    + Close close +
    + +
    +
    +
    + edit +
    +
    +
    + +
    +
    +
    +
    +
    +
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.overview.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.overview.html new file mode 100644 index 00000000000..de50b2640c0 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.overview.html @@ -0,0 +1,3 @@ +
    {{::binding.name}}
    +

    +

    Author: {{::binding.author}}

    \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.thingtypes.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.thingtypes.html new file mode 100644 index 00000000000..2bdfe9ada46 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.binding.thingtypes.html @@ -0,0 +1,27 @@ +
    +
    Supported Things
    + + + + + + +
    {{::thingType.label}}
    +
    {{::thingType.UID}}
    + +
    + + +
    {{::thingType.label}}
    +
    {{::thingType.UID}}
    + +
    + +
    {{::thingType.description}}
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.bindings.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.bindings.html new file mode 100644 index 00000000000..0d8c4949735 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.bindings.html @@ -0,0 +1,34 @@ +
    +
    + refresh bindings +
    + +
    +
    +
    + add +
    +
    +
    + +
    +
    +
    +
    {{::binding.name.substring(0,1).toUpperCase()}}
    +
    +
    +

    + {{::binding.name}} {{::binding.id}} +

    +

    {{::binding.author}}

    +
    +
    + edit + +
    +
    +
    +
    +
    +
    +
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.html index f6e4ac13638..1f43867b18d 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/configuration.html @@ -12,6 +12,7 @@

    {{binding.name}} {{binding.id}}

    +

    {{binding.author}}

    Configure @@ -83,14 +84,14 @@

    {{service.label}}

    {{thing.thingTypeUID?thing.thingTypeUID.split(':')[1].substring(0,1).toUpperCase():''}}
    -
    +

    {{thing.label}} {{thing.statusInfo.status}}

    {{getThingTypeLabel(thing.thingTypeUID)}}

    {{thing.UID}}

    -
    +
    edit delete
    @@ -127,9 +128,9 @@

    {{thing.label}}

    {{details?'hide':'show'}} properties -
    -
    -
    +
    +
    +

    @@ -152,12 +153,14 @@

    Channels

    {{channel.channelType.label?channel.channelType.label.substring(0,1).toUpperCase():'T'}}
    -

    {{channel.label?channel.label:channel.channelType.label}}

    -

    {{channel.id}}

    -

    - {{thing.UID + ':' + channel.id}}content_copy -

    -

    {{channel.itemType}}

    +
    +

    {{channel.label?channel.label:channel.channelType.label}}

    +

    {{channel.id}}

    +

    + {{thing.UID + ':' + channel.id}}content_copy +

    +

    {{channel.itemType}}

    +
    edit {{channel.showItems ? 'unfold_less':'unfold_more'}}
    @@ -237,7 +240,7 @@

    Configuration Parameters

    {{type}} No matches found. {{group.label?group.label:group.name}} No matches found.
    -
    +

    No items defined. @@ -251,14 +254,14 @@

    Configuration Parameters

    {{item.label?item.label.substring(0,1).toUpperCase():item.name.substring(0,1).toUpperCase()}}
    -
    +

    {{item.label}} 

    {{item.name}}content_copy

    {{item.type}}

    -
    +
    edit delete
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/control.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/control.html index cb1f3790091..4e9458f31f3 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/control.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/control.html @@ -1,264 +1,258 @@ -
    +
    refresh
    - - -

    + + + +

    No Things available.

    -
    -
    -
    +
    +
    +
    -

    {{thing.label}}

    +

    {{::thing.label}}

    launch
    -
    -
    -

    {{group.name}}

    +
    +
    +

    {{::group.name}}

    -
    -
    -
    -
    -
    -
    -
    -

    - {{getIcon(item.category)}}{{getLabel(item.category, item.name, 'Switch')}} -

    -
    -
    - -
    +
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category)}}{{::getLabel(item, 'Switch')}} +

    +
    +
    +
    -
    -
    -
    -
    -

    - {{getIcon(item.category, 'brightness_medium')}}{{getLabel(item.category, item.name, 'Dimmer')}} -

    -
    -
    -

    {{item.stateText}}

    -
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category, 'brightness_medium')}}{{::getLabel(item, 'Dimmer')}} +

    - -
    -
    -

    Off/On

    -
    -
    - -
    +
    +

    {{item.stateText}}

    -
    -
    -
    -
    -

    - wb_incandescent{{getLabel(item.category, item.name, 'Color')}} -

    -
    -
    + +
    +
    +

    Off/On

    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + wb_incandescent{{::getLabel(item, 'Color')}} +

    +
    +
    - -
    +
    -
    -
    -

    Brightness

    -
    -
    - -
    +
    +
    +
    +

    Brightness

    -
    -
    -

    Saturation

    -
    -
    - -
    +
    +
    -
    -

    {{item.stateText}}

    +
    +
    +
    +

    Saturation

    +
    +
    +
    -
    -
    -
    -
    -

    - {{getIcon(item.category, 'trending_up')}}{{getLabel(item.category, item.name, 'Value')}} -

    +
    +

    {{item.stateText}}

    +
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category, 'trending_up')}}{{::getLabel(item, 'Value')}} +

    +
    +
    +
    +

    {{item.stateText}}

    -
    -
    -

    {{item.stateText}}

    -
    -
    -

    {{item.stateText}}

    - - check -
    +
    +

    {{item.stateText}}

    + + check
    -
    -
    -
    -
    -

    - {{getIcon(item.category, 'format_align_justify')}}{{getLabel(item.category, item.name, 'Position')}} -

    -
    -
    -
    -

    {{item.stateText}}

    -
    -
    -

    {{item.stateText}}

    - - check -
    -
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category, 'format_align_justify')}}{{::getLabel(item, 'Position')}} +

    -
    -
    -

    Control

    +
    +
    +

    {{::item.stateText}}

    -
    - vertical_align_bottom stop vertical_align_top +
    +

    {{item.stateText}}

    + + check
    -
    -
    -
    -
    -

    - {{getIcon(item.category, 'play_arrow')}}{{getLabel(null, item.name, 'Control')}} -

    -
    -
    - skip_previous - - play_arrow - - pause - - skip_next - -
    -
    -

    {{item.stateText}}

    -
    +
    +
    +

    Control

    +
    +
    + vertical_align_bottom stop vertical_align_top
    -
    -
    -
    -

    - {{getIcon(item.category)}}{{getLabel(item.category, item.name, 'Contact')}} -

    -
    -
    -

    {{item.stateText}}

    -
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category, 'play_arrow')}}{{::getLabel(item, 'Control')}} +

    +
    +
    + skip_previous + + play_arrow + + pause + + skip_next + +
    +
    +

    {{item.stateText}}

    -
    -
    -
    +
    +
    +
    +

    - place{{getLabel(item.category, item.name, 'Location')}} + {{::getIcon(item.category)}}{{::getLabel(item, 'Contact')}}

    -
    -
    - -
    +
    +

    {{item.stateText}}

    -
    -
    -

    {{formattedState}}

    -
    -
    -

    {{formattedState}}

    - - check -
    +
    +
    +
    +
    +
    +

    + place{{::getLabel(item, 'Location')}} +

    +
    +
    +
    +
    -
    -
    -
    -
    -

    - camera_alt{{getLabel(item.category,item.name,'Camera image')}} -

    -
    -
    - autorenew -
    +
    +
    +

    {{formattedState}}

    -
    -
    - - -
    No image is currently present
    -
    -
    +
    +

    {{formattedState}}

    + + check
    -
    -
    -

    - {{getIcon(item.category)}}{{getLabel(item.category, item.name, 'Value')}} +

    +
    +
    +
    +
    +

    + camera_alt{{::getLabel(item, 'Camera image')}}

    -
    -
    - {{item.stateText}} -
    -
    - {{item.stateText}} - - check -
    -
    -
    -
    - {{item.stateText}} -
    -
    - {{item.stateText}} - - check +
    + autorenew +
    +
    +
    +
    + + +
    No image is currently present
    +
    +
    +
    +
    +
    +
    +

    + {{::getIcon(item.category)}}{{::getLabel(item, 'Value')}} +

    +
    +
    + {{item.stateText}} +
    +
    + {{item.stateText}} + + check
    - -
    +
    +
    + {{item.stateText}} +
    +
    + {{item.stateText}} + + check +
    +
    + +
    -
    +
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/directive.searchField.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/directive.searchField.html new file mode 100644 index 00000000000..e413a7f3576 --- /dev/null +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/directive.searchField.html @@ -0,0 +1,3 @@ +search + +close diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/extensions.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/extensions.html index 733af05fb8f..60f0a2092f0 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/extensions.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/extensions.html @@ -16,8 +16,10 @@
    {{extension.label.substring(0,1).toUpperCase()}}
    -

    {{extension.label}}

    -

    {{extension.id}} - {{extension.version}}

    +
    +

    {{extension.label}}

    +

    {{extension.id}} - {{extension.version}}

    +
    Install Uninstall @@ -30,7 +32,7 @@

    -
    +
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/include.inbox.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/include.inbox.html index 2348e4f017a..0d5c18866eb 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/include.inbox.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/include.inbox.html @@ -16,11 +16,13 @@
    done
    -

    - {{discoveryResult.label}} IGNORED -

    -

    {{getThingTypeLabel(discoveryResult.thingTypeUID)}}

    -

    {{discoveryResult.thingUID}}

    +
    +

    + {{discoveryResult.label}} IGNORED +

    +

    {{getThingTypeLabel(discoveryResult.thingTypeUID)}}

    +

    {{discoveryResult.thingUID}}

    +
    visibility visibility_off delete
    diff --git a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/item.state.dropdown.html b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/item.state.dropdown.html index 4b73cfa7b44..b08cbec573f 100644 --- a/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/item.state.dropdown.html +++ b/extensions/ui/org.eclipse.smarthome.ui.paper/web-src/partials/item.state.dropdown.html @@ -1,6 +1,6 @@
    - {{option.label}} + {{::option.label}}
    diff --git a/extensions/voice/org.eclipse.smarthome.voice.mactts/src/main/java/org/eclipse/smarthome/voice/mactts/internal/MacTTSService.java b/extensions/voice/org.eclipse.smarthome.voice.mactts/src/main/java/org/eclipse/smarthome/voice/mactts/internal/MacTTSService.java index 7f0c7c99be0..233f44a781e 100644 --- a/extensions/voice/org.eclipse.smarthome.voice.mactts/src/main/java/org/eclipse/smarthome/voice/mactts/internal/MacTTSService.java +++ b/extensions/voice/org.eclipse.smarthome.voice.mactts/src/main/java/org/eclipse/smarthome/voice/mactts/internal/MacTTSService.java @@ -102,7 +102,7 @@ private final Set initVoices() { voices.add(new MacTTSVoice(nextLine)); } } catch (IOException e) { - logger.error("Error while executing the 'say -v ?' command: " + e.getMessage()); + logger.error("Error while executing the 'say -v ?' command: {}", e.getMessage()); } finally { IOUtils.closeQuietly(bufferedReader); } diff --git a/features/karaf/esh-core/src/main/feature/feature.xml b/features/karaf/esh-core/src/main/feature/feature.xml index e89c7662ae3..69d39ecd110 100644 --- a/features/karaf/esh-core/src/main/feature/feature.xml +++ b/features/karaf/esh-core/src/main/feature/feature.xml @@ -17,38 +17,89 @@ esh.tp;filter:="(feature=jax-rs)" mvn:org.eclipse.smarthome.config/org.eclipse.smarthome.config.core/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.config.core.ConfigDescriptionRegistry + osgi.service;objectClass=org.eclipse.smarthome.config.core.i18n.ConfigI18nLocalizationService + + osgi.service;objectClass=org.eclipse.smarthome.config.core.status.ConfigStatusService + mvn:org.eclipse.smarthome.config/org.eclipse.smarthome.config.discovery/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.config.discovery.DiscoveryServiceRegistry + + + osgi.service;objectClass=org.eclipse.smarthome.config.discovery.inbox.Inbox + + mvn:org.eclipse.smarthome.config/org.eclipse.smarthome.config.dispatch/${project.version} mvn:org.eclipse.smarthome.config/org.eclipse.smarthome.config.xml/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.core.binding.BindingInfoRegistry + osgi.service;objectClass=org.eclipse.smarthome.core.i18n.TranslationProvider + + osgi.service;objectClass=org.eclipse.smarthome.core.items.ItemFactory + osgi.service;objectClass=org.eclipse.smarthome.core.items.ItemRegistry + + osgi.service;objectClass=org.eclipse.smarthome.core.items.ManagedItemProvider + mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.autoupdate/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.binding.xml/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.id/${project.version} + mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.persistence/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.core.persistence.PersistenceServiceRegistry + + mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.scheduler/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.thing/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.ManagedThingProvider + osgi.service;objectClass=org.eclipse.smarthome.core.thing.ThingRegistry + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.firmware.FirmwareRegistry + + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.firmware.FirmwareUpdateService + + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.i18n.ThingStatusInfoI18nLocalizationService + osgi.service;objectClass=org.eclipse.smarthome.core.thing.i18n.ThingTypeI18nLocalizationService osgi.service;objectClass=org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.link.ManagedItemChannelLinkProvider + + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.link.ThingLinkManager + + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.type.ChannelTypeRegistry + + + osgi.service;objectClass=org.eclipse.smarthome.core.thing.type.ThingTypeRegistry + mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.thing.xml/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.transform/${project.version} @@ -63,7 +114,12 @@ mvn:org.eclipse.smarthome.io/org.eclipse.smarthome.io.monitor/${project.version} mvn:org.eclipse.smarthome.io/org.eclipse.smarthome.io.net/${project.version} mvn:org.eclipse.smarthome.io/org.eclipse.smarthome.io.rest/${project.version} + mvn:org.eclipse.smarthome.io/org.eclipse.smarthome.io.rest.core/${project.version} + + osgi.service;objectClass=org.eclipse.smarthome.io.rest.core.config.ConfigurationService + + mvn:org.eclipse.smarthome.io/org.eclipse.smarthome.io.rest.sse/${project.version} mvn:org.eclipse.smarthome.model/org.eclipse.smarthome.model.core/${project.version} diff --git a/pom.xml b/pom.xml index 272b6440c54..2ff5850dc80 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ 2.9.2 4.0.3 1.2.8 + 2.1.0 1.8 UTF-8 @@ -156,7 +157,16 @@ org.apache.felix.scr.ds-annotations ${ds-annotations.version} + + org.eclipse.jdt + org.eclipse.jdt.annotation + ${jdt-annotations.version} + + + -err:+nullAnnot(org.eclipse.jdt.annotation.Nullable|org.eclipse.jdt.annotation.NonNull|org.eclipse.jdt.annotation.NonNullByDefault),+inheritNullAnnot + -warn:+null,+inheritNullAnnot,+nullAnnotConflict,+nullUncheckedConversion,+nullAnnotRedundant,+nullDereference + diff --git a/targetplatform/EclipseSmartHome.setup b/targetplatform/EclipseSmartHome.setup index 314d39cd69c..25f7a80a44b 100644 --- a/targetplatform/EclipseSmartHome.setup +++ b/targetplatform/EclipseSmartHome.setup @@ -65,6 +65,14 @@ xsi:type="setup:PreferenceTask" key="/instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations" value="enabled"/> + + + url="http://dist.springsource.org/snapshot/GRECLIPSE/e4.7/"/> diff --git a/targetplatform/SmartHome-Runtime.launch b/targetplatform/SmartHome-Runtime.launch index 9f9a05e8124..d734fcfa4ef 100644 --- a/targetplatform/SmartHome-Runtime.launch +++ b/targetplatform/SmartHome-Runtime.launch @@ -19,20 +19,12 @@ - + - + diff --git a/targetplatform/smarthome.target b/targetplatform/smarthome.target index bd18af80dd8..9ed0d12405a 100644 --- a/targetplatform/smarthome.target +++ b/targetplatform/smarthome.target @@ -1,5 +1,5 @@ - + @@ -124,7 +124,7 @@ - + @@ -159,18 +159,22 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/tools/archetype/binding.test/src/main/resources/archetype-resources/META-INF/MANIFEST.MF b/tools/archetype/binding.test/src/main/resources/archetype-resources/META-INF/MANIFEST.MF index e6e1a48544a..c65ad522b7f 100644 --- a/tools/archetype/binding.test/src/main/resources/archetype-resources/META-INF/MANIFEST.MF +++ b/tools/archetype/binding.test/src/main/resources/archetype-resources/META-INF/MANIFEST.MF @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 -Bundle-Name: ${bindingIdCamelCase} Binding Tests +Bundle-Name: ${bindingIdCamelCase} Binding Tests Bundle-SymbolicName: ${artifactId};singleton:=true Bundle-Vendor: ${vendorName} Bundle-Version: ${version.replaceAll("-SNAPSHOT", ".qualifier")} @@ -8,16 +8,20 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: ${package} Import-Package: ${package}, ${package}.handler, - org.slf4j, + org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.storage, org.eclipse.smarthome.test.java, + org.eclipse.smarthome.test.storage, org.hamcrest;core=split, - org.mockito, org.junit;version="4.0.0", + org.mockito, + org.osgi.framework, org.osgi.service.device, - org.osgi.framework + org.slf4j Require-Bundle: org.junit,org.mockito,org.hamcrest -Export-Package: ${package}.internal;x-internal:=true, - ${package};uses:="org.eclipse.smarthome.test" +Export-Package: ${package};uses:="org.eclipse.smarthome.test" + diff --git a/tools/archetype/binding.test/src/main/resources/archetype-resources/build.properties b/tools/archetype/binding.test/src/main/resources/archetype-resources/build.properties index 8a49b2d3a06..3f0a98dbc94 100644 --- a/tools/archetype/binding.test/src/main/resources/archetype-resources/build.properties +++ b/tools/archetype/binding.test/src/main/resources/archetype-resources/build.properties @@ -1,4 +1,5 @@ source.. = src/test/java/ output.. = target/test-classes bin.includes = META-INF/,\ - . + .,\ + about.html diff --git a/tools/archetype/binding.test/src/main/resources/archetype-resources/pom.xml b/tools/archetype/binding.test/src/main/resources/archetype-resources/pom.xml index 8aa4dc28c51..8405665e624 100644 --- a/tools/archetype/binding.test/src/main/resources/archetype-resources/pom.xml +++ b/tools/archetype/binding.test/src/main/resources/archetype-resources/pom.xml @@ -6,12 +6,12 @@ ${namespace}.binding pom - ${version} + @${version} - ${groupId} + @${groupId} ${rootArtifactId} - ${version} + @${version} eclipse-test-plugin ${bindingIdCamelCase} Binding Tests diff --git a/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__HandlerTest.java b/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__HandlerTest.java index 3646da7f650..9664fdcb2c4 100644 --- a/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__HandlerTest.java +++ b/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__HandlerTest.java @@ -11,25 +11,26 @@ package ${package}; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; import ${package}.handler.${bindingIdCamelCase}Handler; import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; /** -* Tests cases for {@link ${bindingIdCamelCase}Handler}. The tests provide mocks for supporting entities using Mockito. +* Tests cases for {@link ${bindingIdCamelCase}Handler}. The tests provide mocks for supporting entities using Mockito. * -* @author ${author} - Initial contribution +* @author ${author} - Initial contribution */ public class ${bindingIdCamelCase}HandlerTest { @@ -62,7 +63,7 @@ public void initializeShouldCallTheCallback() { verify(callback).statusUpdated(eq(thing), statusInfoCaptor.capture()); // assert that the ThingStatusInfo given to the callback was build with the ONLINE status: ThingStatusInfo thingStatusInfo = statusInfoCaptor.getValue(); - Assert.assertThat(thingStatusInfo.getStatus(), is(equalTo(ThingStatus.ONLINE))); + assertThat(thingStatusInfo.getStatus(), is(equalTo(ThingStatus.ONLINE))); } } diff --git a/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__OSGiTest.java b/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__OSGiTest.java index c618e7487fd..8b57de439ed 100644 --- a/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__OSGiTest.java +++ b/tools/archetype/binding.test/src/main/resources/archetype-resources/src/test/java/__bindingIdCamelCase__OSGiTest.java @@ -12,36 +12,38 @@ import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import ${package}.handler.${bindingIdCamelCase}Handler; +import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ManagedThingProvider; import org.eclipse.smarthome.core.thing.ThingProvider; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.builder.BridgeBuilder; import org.eclipse.smarthome.test.java.JavaOSGiTest; import org.eclipse.smarthome.test.storage.VolatileStorageService; +import org.junit.After; import org.junit.Before; import org.junit.Test; /** -* Tests cases for {@link ${bindingIdCamelCase}Handler}. +* Tests cases for {@link ${bindingIdCamelCase}Handler}. * -* @author ${author} - Initial contribution +* @author ${author} - Initial contribution */ public class ${bindingIdCamelCase}OSGiTest extends JavaOSGiTest { - private final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("${bindingId}", "bridge"); + private static final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("${bindingId}", "bridge"); private ManagedThingProvider managedThingProvider; - private VolatileStorageService volatileStorageService = new VolatileStorageService(); + private final VolatileStorageService volatileStorageService = new VolatileStorageService(); private Bridge bridge; - + @Before public void setUp() { registerService(volatileStorageService); managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class); - bridge = BridgeBuilder.create(BRIDGE_THING_TYPE_UID, "1").withLabel("My Bridge").build(); + bridge = BridgeBuilder.create(BRIDGE_THING_TYPE_UID, "1").withLabel("My Bridge").build(); } @After @@ -54,7 +56,7 @@ public void tearDown() { public void creationOf${bindingIdCamelCase}Handler() { assertThat(bridge.getHandler(), is(nullValue())); managedThingProvider.add(bridge); - waitForAssert(() -> assertThat(bridge.getHandler(), is(notNullValue()))); + waitForAssert(() -> assertThat(bridge.getHandler(), is(notNullValue()))); } } diff --git a/tools/archetype/binding/src/main/resources/archetype-resources/META-INF/MANIFEST.MF b/tools/archetype/binding/src/main/resources/archetype-resources/META-INF/MANIFEST.MF index 46a0edfffb0..b2b6f52006c 100644 --- a/tools/archetype/binding/src/main/resources/archetype-resources/META-INF/MANIFEST.MF +++ b/tools/archetype/binding/src/main/resources/archetype-resources/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Vendor: ${vendorName} Bundle-Version: ${version.replaceAll("-SNAPSHOT", ".qualifier")} Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: . -Import-Package: +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, ${package}, ${package}.handler, org.eclipse.smarthome.config.core, @@ -16,7 +16,9 @@ Import-Package: org.eclipse.smarthome.core.thing.binding.builder, org.eclipse.smarthome.core.thing.type, org.eclipse.smarthome.core.types, + org.osgi.service.component.annotations;resolution:=optional, org.slf4j Service-Component: OSGI-INF/*.xml Export-Package: ${package}, ${package}.handler +Bundle-ActivationPolicy: lazy diff --git a/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/.gitignore b/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b81c7954b78 --- /dev/null +++ b/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/.gitignore @@ -0,0 +1 @@ +*.xml \ No newline at end of file diff --git a/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/__bindingIdCamelCase__HandlerFactory.xml b/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/__bindingIdCamelCase__HandlerFactory.xml deleted file mode 100644 index 31324d3a0bd..00000000000 --- a/tools/archetype/binding/src/main/resources/archetype-resources/OSGI-INF/__bindingIdCamelCase__HandlerFactory.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - diff --git a/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/handler/__bindingIdCamelCase__Handler.java b/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/handler/__bindingIdCamelCase__Handler.java index db6910761d3..b644a1a0c1b 100644 --- a/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/handler/__bindingIdCamelCase__Handler.java +++ b/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/handler/__bindingIdCamelCase__Handler.java @@ -12,6 +12,7 @@ import static ${package}.${bindingIdCamelCase}BindingConstants.*; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -30,7 +31,7 @@ public class ${bindingIdCamelCase}Handler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(${bindingIdCamelCase}Handler.class); - public ${bindingIdCamelCase}Handler(Thing thing) { + public ${bindingIdCamelCase}Handler(@NonNull Thing thing) { super(thing); } diff --git a/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/internal/__bindingIdCamelCase__HandlerFactory.java b/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/internal/__bindingIdCamelCase__HandlerFactory.java index 55e8d0e37cf..c7f9147cae3 100644 --- a/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/internal/__bindingIdCamelCase__HandlerFactory.java +++ b/tools/archetype/binding/src/main/resources/archetype-resources/src/main/java/internal/__bindingIdCamelCase__HandlerFactory.java @@ -20,6 +20,8 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; /** * The {@link ${bindingIdCamelCase}HandlerFactory} is responsible for creating things and thing @@ -27,6 +29,7 @@ * * @author ${author} - Initial contribution */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPolicy = ConfigurationPolicy.OPTIONAL, name = "binding.${bindingId}") public class ${bindingIdCamelCase}HandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_SAMPLE);

    UNINITIALIZEDNONENo further status details available.
    channel-groupsThe channel groups defining the channels the bridge/Thing provides (optional).
    channel-group.idAn identifier of the channel group the bridge/Thing provides (mandatory).
    channel-group.typeIdAn identifier of the channel group type definition the bridge/Thing provides (mandatory).
    propertiesName/value pairs for properties to be set to the thing (optional).
    representation-propertyThe name of the property that contains a unique identifier of the thing (optional).
    config-descriptionThe configuration description for the bridge/Thing within the ConfigDescriptionRegistry (optional).
    config-description-refThe reference to a configuration description for the bridge/Thing within the ConfigDescriptionRegistry (optional).
    config-description-ref.uriThe URI of the configuration description for the bridge/Thing within the ConfigDescriptionRegistry (mandatory).
    channel-group-type.advancedThe flag indicating if this channel group contains advanced functionalities which should be typically not shown in the basic view of user interfaces (optional, default: false).
    labelA human-readable label for the channel group (mandatory).
    descriptionA human-readable description for the channel group (optional).
    categoryThe category for the channel group, e.g. TEMPERATURE (optional).
    channelsThe channels the bridge/Thing provides (mandatory).
    channel.idAn identifier of the channel the bridge/Thing provides (mandatory).
    channel.typeIdAn identifier of the channel type definition the bridge/Thing provides (mandatory).