Custom binding does not work

Hi All,

I’m trying to make my binding work in OH 1.8.3 for a day but nothing. I have created the binding according to the description on GitHub. I use Export -> Deployable plugins and fragments in Eclipse Neon to generate the jar.

Although KeContactPActivator.start is called by OH, KeContactPBinding.activate is never called. If I wait more, the situation remains the same.

What do I do wrong?

Thank you in advance, best regards: Balazs Bamer

Here is openHAB log:

Launching the openHAB runtime...
osgi> 2016-11-16 14:00:53.659 [INFO ] [.o.core.internal.CoreActivator] - openHAB runtime has been started (v1.8.3).
2016-11-16 14:00:55.246 [INFO ] [o.o.i.s.i.DiscoveryServiceImpl] - mDNS service has been started
2016-11-16 14:00:55.406 [INFO ] [o.o.i.s.i.DiscoveryServiceImpl] - Service Discovery initialization completed.
2016-11-16 14:00:57.535 [INFO ] [penhab.io.rest.RESTApplication] - Started REST API at /rest
2016-11-16 14:01:04.849 [INFO ] [c.internal.ModelRepositoryImpl] - Loading model 'default.sitemap'
2016-11-16 14:01:05.080 [INFO ] [c.internal.ModelRepositoryImpl] - Loading model 'experiment.items'
2016-11-16 14:01:09.671 [INFO ] [o.o.c.j.i.e.s.ScriptManager   ] - Available engines:
2016-11-16 14:01:09.677 [INFO ] [o.o.c.j.i.e.s.ScriptManager   ] - Oracle Nashorn
2016-11-16 14:01:09.677 [INFO ] [o.o.c.j.i.e.s.ScriptManager   ] - Groovy Scripting Engine
2016-11-16 14:01:09.679 [INFO ] [o.c.j.i.e.scriptmanager.Script] - Loading Script kebacurrentlimittest.groovy
2016-11-16 14:01:09.705 [INFO ] [o.c.j.i.e.scriptmanager.Script] - EngineName: Groovy Scripting Engine
2016-11-16 14:01:10.750 [INFO ] [o.o.c.j.i.e.s.ScriptManager   ] - Engine found for File: kebacurrentlimittest.groovy
2016-11-16 14:01:10.825 [WARN ] [k.internal.KeContactPActivator] - KeContactP binding has been started.
2016-11-16 14:01:10.873 [WARN ] [ContactPGenericBindingProvider] - getBindingType
2016-11-16 14:01:10.873 [WARN ] [ContactPGenericBindingProvider] - getBindingType
2016-11-16 14:01:10.874 [WARN ] [ContactPGenericBindingProvider] - getBindingType
2016-11-16 14:01:10.874 [WARN ] [ContactPGenericBindingProvider] - getBindingType
2016-11-16 14:01:10.874 [WARN ] [ContactPGenericBindingProvider] - validateItemType ladestrom (Type=NumberItem, State=Uninitialized)10.128.3.131
2016-11-16 14:01:10.875 [WARN ] [ContactPGenericBindingProvider] - processBindingConfiguration experiment.itemsladestrom (Type=NumberItem, State=Uninitialized)10.128.3.131
2016-11-16 14:01:10.975 [INFO ] [.o.u.w.i.servlet.WebAppServlet] - Started Classic UI at /classicui/openhab.app
2016-11-16 14:02:00.032 [INFO ] [.o.c.j.i.engine.TimeTriggerJob] - TimeTrigger for rule: KebaCurrentLimitTest@2ee540bd, scriptName: kebacurrentlimittest.groovy
2016-11-16 14:02:00.034 [WARN ] [.m.jsr223.KebaCurrentLimitTest] - About to set current limit
2016-11-16 14:02:00.050 [INFO ] [runtime.busevents             ] - ladestrom received command 33333
osgi> exit
Really want to stop Equinox? (y/n; default=y)  y
2016-11-16 14:02:11.019 [INFO ] [penhab.io.rest.RESTApplication] - Stopped REST API

Here are the important parts:

Groovy rule to send a command to the binding

import org.openhab.core.jsr223.internal.shared.*
import groovy.transform.CompileStatic
import org.slf4j.Logger;
import org.openhab.core.items.Item
import org.openhab.core.items.ItemRegistry

Global.itemRegistry = this.ItemRegistry
Global.pe = this.pe

class Global {
    static ItemRegistry itemRegistry
    static Class pe
}

@CompileStatic
class KebaCurrentLimitTest implements Rule {
    protected static Logger log = Openhab.getLogger("KebaCurrentLimitTest")

    public KebaCurrentLimitTest() {   }

    java.util.List<?> getEventTrigger() {
        return [
            new TimerTrigger("0 * * * * ?")
        ]
    }

    void execute(Event event) {
        log.warn("About to set current limit")
        Item ladestrom = Global.itemRegistry.getItem("ladestrom");
        Openhab.sendCommand(ladestrom, "33333")
    }
}

@CompileStatic
RuleSet getRules() {
    return new RuleSet(new KebaCurrentLimitTest())
}

experiment.items

Number ladestrom     "Ladestrom [%d]" (gemischt) {kecontactp="10.128.3.131"}

default.sitemap

sitemap default label="Demo House"
{
    Frame label="gemischt" {
        Text item=Date
        Number item=ladestrom
    }
}

MANIFEST.MF

Manifest-Version: 1.0
Private-Package: org.openhab.binding.kecontactp.internal
Ignore-Package: org.openhab.binding.kecontactp.internal
Bundle-License: http://www.eclipse.org/legal/epl-v10.html
Bundle-Name: openHAB KeContactP Binding
Bundle-SymbolicName: org.openhab.binding.kecontactp
Bundle-Vendor: openHAB.org
Bundle-Version: 1.8.3.qualifier
Bundle-Activator: org.openhab.binding.kecontactp.internal.KeContactPActivator
Bundle-ManifestVersion: 2
Bundle-Description: This is the KeContactP binding of the open Home Aut
 omation Bus (openHAB)
Import-Package: org.apache.commons.lang,
 org.openhab.core.binding,
 org.openhab.core.events,
 org.openhab.core.items,
 org.openhab.core.library.items,
 org.openhab.core.library.types,
 org.openhab.core.types,
 org.openhab.model.item.binding,
 org.osgi.framework,
 org.osgi.service.component,
 org.osgi.service.event,
 org.slf4j
Export-Package: org.openhab.binding.kecontactp
Bundle-DocURL: http://www.openhab.org
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Service-Component: OSGI-INF/binding.xml, OSGI-INF/genericbindingprovider.xml
Bundle-ClassPath: .

build.properties

source.. = src/main/java/,\
           src/main/resources/
bin.includes = META-INF/,\
               .,\
               OSGI-INF/
output.. = target/classes/

binding.xml

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.2.0" activate="activate" modified="modified" deactivate="deactivate" name="org.openhab.binding.kecontactp.binding" factory="org.openhab.kecontactp" immediate="false" configuration-pid="org.openhab.kecontactp" configuration-policy="optional">
    <implementation class="org.openhab.binding.kecontactp.internal.KeContactPBinding" />
    <service>
        <provide interface="org.osgi.service.cm.ManagedService" />
        <provide interface="org.osgi.service.event.EventHandler" />
    </service>
    <property name="service.pid" type="String" value="org.openhab.kecontactp" />
    <property name="event.topics" type="String" value="openhab/*" />
    <reference bind="setEventPublisher" cardinality="1..1"
        interface="org.openhab.core.events.EventPublisher" name="EventPublisher" policy="dynamic" unbind="unsetEventPublisher" />
    <reference bind="addBindingProvider" cardinality="1..n"
        interface="org.openhab.binding.kecontactp.KeContactPBindingProvider" name="KeContactPBindingProvider" policy="dynamic" unbind="removeBindingProvider" />
</scr:component>

genericbindingprovider.xml

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.openhab.binding.kecontactp.genericbindingprovider">
   <implementation class="org.openhab.binding.kecontactp.internal.KeContactPGenericBindingProvider"/>
   <service>
      <provide interface="org.openhab.model.item.binding.BindingConfigReader"/>
      <provide interface="org.openhab.binding.kecontactp.KeContactPBindingProvider"/>
   </service>
</scr:component>

KeContactPBindingProvider

package org.openhab.binding.kecontactp;

import java.net.InetAddress;
import org.openhab.core.binding.BindingProvider;

public interface KeContactPBindingProvider extends BindingProvider {
    InetAddress getAddress(String itemName);

    int getPort(String itemName);
}

KeContactPActivator.java

package org.openhab.binding.kecontactp.internal;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class KeContactPActivator implements BundleActivator {
   private static Logger logger = LoggerFactory.getLogger(KeContactPActivator.class); 
    private static BundleContext context;
    
    public void start(BundleContext bc) throws Exception {
        context = bc;
        logger.warn("KeContactP binding has been started.");
    }

    public void stop(BundleContext bc) throws Exception {
        context = null;
        logger.warn("KeContactP binding has been stopped.");
    }
    
    public static BundleContext getContext() {
        logger.warn("getContext");
        return context;
    }
}

KeContactPBinding.java

package org.openhab.binding.kecontactp.internal;

import java.util.Map;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;

import org.openhab.binding.kecontactp.KeContactPBindingProvider;

import org.apache.commons.lang.StringUtils;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.json.JSONObject;

public class KeContactPBinding extends AbstractActiveBinding<KeContactPBindingProvider> {
    private static final Logger logger =  LoggerFactory.getLogger(KeContactPBinding.class);
    private BundleContext bundleContext;
    private int timeout = 1;
    
    public KeContactPBinding() {}    
    
    public void activate(final BundleContext bundleContext, final Map<String, Object> configuration) {
        logger.warn("activate");
        this.bundleContext = bundleContext;
        modified(configuration);
        setProperlyConfigured(true);
    }
    
    public void modified(final Map<String, Object> configuration) {
        logger.warn("modified");
        // PARSE CONFIG HERE
    }
    
    public void deactivate(final int reason) {
        logger.warn("deactivate");
        this.bundleContext = null;
    }

    @Override
    protected long getRefreshInterval() {
        return 0;
    }

    @Override
    protected String getName() {
        return "KeContactP UDP receiver";
    }
    
    @Override
    protected void execute() {
        logger.warn("execute() method is called!");
        for (KeContactPBindingProvider provider : providers) {
            for (String itemName : provider.getItemNames()) {
                InetAddress address = provider.getAddress(itemName);
                int port = provider.getPort(itemName);
                logger.warn("execute address: " + address + "  port: " + port);
            }
        }
    }

    @Override
    protected void internalReceiveCommand(String itemName, Command command) {
        logger.warn("internalReceiveCommand() method is called!", itemName, command);        
        for (KeContactPBindingProvider provider : this.providers) {
            InetAddress address = provider.getAddress(itemName);
            int port = provider.getPort(itemName);
    // DO SOMETHING HERE
            logger.warn("internalReceiveCommand address: " + address + "  port: " + port);
        }
    }
    
    @Override
    protected void internalReceiveUpdate(String itemName, State newState) {
        logger.error("internalReceiveUpdate({},{}) is called but not intended to be implemented!", itemName, newState);
    }
}

KeContactPGenericBindingProvider.java

package org.openhab.binding.kecontactp.internal;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.openhab.binding.kecontactp.KeContactPBindingProvider;
import org.openhab.core.binding.BindingConfig;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.NumberItem;
import org.openhab.model.item.binding.AbstractGenericBindingProvider;
import org.openhab.model.item.binding.BindingConfigParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class KeContactPGenericBindingProvider extends AbstractGenericBindingProvider implements KeContactPBindingProvider {

    private static final Logger logger = LoggerFactory.getLogger(KeContactPGenericBindingProvider.class);
    
    public String getBindingType() {
        logger.warn("getBindingType");
        return "kecontactp";
    }

    @Override
    public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException {
        logger.warn("validateItemType " + item + bindingConfig);
        if (!(item instanceof NumberItem)) {
            throw new BindingConfigParseException("item '" + item.getName()
                    + "' is of type '" + item.getClass().getSimpleName()
                    + "', only NumberItems are allowed - please check your *.items configuration");
        }
    }
    
    @Override
    public void processBindingConfiguration(String context, Item item, String bindingConfig) throws BindingConfigParseException {
        super.processBindingConfiguration(context, item, bindingConfig);
        logger.warn("processBindingConfiguration " + context + item + bindingConfig);
        KeContactPBindingConfig config = new KeContactPBindingConfig();
   // PARSE CONFIG HERE             
        addBindingConfig(item, config);        
    }
    
    public InetAddress getAddress(String itemName) {
        logger.warn("getAddress " + itemName);
        KeContactPBindingConfig config = (KeContactPBindingConfig) bindingConfigs.get(itemName);
            return config.address;
    }

    public int getPort(String itemName) {
        logger.warn("getPort " + itemName);
        KeContactPBindingConfig config = (KeContactPBindingConfig) bindingConfigs.get(itemName);
            return config.port;
    }
    
    static private class KeContactPBindingConfig implements BindingConfig {
        public InetAddress address;
        public int port = 7090;
    }
}

Hi All,

Some additional info: the binding is active.

I have tried Modbus binding in a simple way:

Number ladestrom    "Ladestrom [%d]" {kecontactp="10.128.3.131"}
Switch date          {exec=">[date:/opt/openhab-werk/date.sh]"}
Switch losGroovy          "Abfragen-Groovy"
Switch losXtext          "Abfragen-XText"
Number Boiler_Status        {modbus="boiler:4"}

a traditional rule:

rule "xtext"
when
    Item losXtext changed
then
    logWarn("org.openhab", "XText: about to set current limit")
    sendCommand(ladestrom, 33333)
    sendCommand(date, ON)
    postUpdate(Boiler_Status, 0)
end

The log shows there is some other problem than my binding. Although the Modbus binding gets called, it does nothing. I know because it should report connection error, since the device is disconnected. It reports nothing, only when polling occurs.

What can make several bindings fail?

Best regards: Balazs

Best

Hi All,

I couldn’t find out why my binding fails. No matter what I tried, from which other binding I tried to copy the metadata, nothing. All the generated stuff and included metadata, also annotations matched the donor binding.

I ended up by taking the exec binding, renaming it and step-by-step implemented my functionality, meanwhile I tested operation after each step. This way now it works perfect.

best regards: Balazs Bamer