element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Forget Me Not Design Challenge
  • Challenges & Projects
  • Design Challenges
  • Forget Me Not Design Challenge
  • More
  • Cancel
Forget Me Not Design Challenge
Blog Cam-e-lot - Round 9 - Man vs Binding (2)
  • Blog
  • Forum
  • Documents
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: amgalbu
  • Date Created: 13 Sep 2014 1:26 PM Date Created
  • Views 374 views
  • Likes 0 likes
  • Comments 0 comments
  • forget_me_not
  • iot_cam-e-lot
Related
Recommended

Cam-e-lot - Round 9 - Man vs Binding (2)

amgalbu
amgalbu
13 Sep 2014

After installing everything we need to develop a binding for a new enOcean profile into OpenHAB, let's start to implement such a new profile

OpenHAB binding

OpenHAB has an architecture based on a bus where the OpenHAB runtime components write messages and receive notifications. Bindings are one of those components specifically designed to communicate with a external devices using a certain protocol.

Here is an overview of the OpenHAB architecture

  

There is a tutorial here that provides an in-depth description of how to create a new binding. However, I'm not going to create a new binding from scratch but extend the existing Aleoncean binding.

First things first

Before proceeding, I need to select the proper profile to adhere to. After reading through the enOcean equipment profiles document (available here), I selected the D2-01 profile, that is described as "Electronic switches and dimmers with energy".

I am going to implement for the moment only the part that relates to the switches. In particular, I will implement the following frames

CMD 0x01: Actuator set output

This message controls switching / dimming of one or all channels of an actuator

CMD 0x03: Actuator status query

This message requests the status of one or all channels of an actuator

CMD 0x04: Actuator status response

This message is sent by an actuator if one of the following event occurs

  1. 1.       status of a channel has changed
  2. 2.       a "Actuator status query" message has been received

 

Implementing the enOcean profile

Important note: here I'm NOT referrig to the Aleoncean binding library (available here) but the Aleoncean library (available here) that provides the framework the binding has been built on

To implement the new enOcean profile, I need to modify the Aleoncean library. First of all, let's setup the development environment

  1. 1.       Since I've not been able to import the Aleoncean library project into Eclipse, I downloaded and installed Maven to build it (see this tutorial for a detailed explanation about how to install Maven)
  2. 2.       download the Aleoncean library from here. Unzip the file (for example in C:\Documents and Settings\Administrator\aleoncean-master)
  3. 3.       open a command window in the Aleoncean source directory (C:\Documents and Settings\Administrator\aleoncean-master) and type "mvn". The library should build without errors

 

Now we can start with the changes to the source code. I created a new class name RemoteDeviceEEPD20100, which extends the StandardDevice class and implements the RemoteDevice interface.

First of all, I will override the method to access a specific device value based on the settings in the items configuration file. The Aleoncean binding communicates with the device classes through methods called

 

    @Override

    public Object getByParameter(final DeviceParameter parameter) throws IllegalDeviceParameterException;

 

    @Override

    public void setByParameter(final DeviceParameter parameter, final Object value) throws IllegalDeviceParameterException;

 

For example, if in the items configuration file there is a line like this

 

Switch Input_Flame "Flame detected" (gGF) {aleoncean="REMOTEID=01:85:3A:3A,TYPE=RD_D2-01-00,PARAMETER=SWITCH_0"}

 

the binding will create the proper device class based on the content of the TYPE field and invoke the getDeviceParameter method using the content of the PARAMETER field as argument

 

getDeviceParameter(DeviceParameter.SWITCH_0);

 

to get the current status of the actuator 0.  The same happens when the binding needs to set the value

 

setDeviceParameter(DeviceParameter.SWITCH_0, true);

 

First of all, let's add the new values to the DeviceParameter enum

public enum DeviceParameter {

 

    BUTTON_DIM_A,

    BUTTON_DIM_B,

    ...

    TEMPERATURE_CONTROL_CUR_TEMP,

    WINDOW_HANDLE_POSITION,

    // New code begins here

    SWITCH_0,

    SWITCH_1,

    SWITCH_2,

    SWITCH_3,

    SWITCH_4,

    SWITCH_5,

    SWITCH_6,

    SWITCH_7,

    SWITCH_8,

    SWITCH_9,

    SWITCH_10,

    SWITCH_11,

    SWITCH_12,

    SWITCH_13,

    SWITCH_14,

    SWITCH_15,

    SWITCH_16,

    SWITCH_17,

    SWITCH_18,

    SWITCH_19,

    SWITCH_20,

    SWITCH_21,

    SWITCH_22,

    SWITCH_23,

    SWITCH_24,

    SWITCH_25,

    SWITCH_26,

    SWITCH_27,

    SWITCH_28,

    SWITCH_29,

    // New code ends here

 

    TMP_RECV_SERVICE_ON,

    TMP_RECV_ENERGY_INPUT_ENABLED,

 

I added 30 switches because this is the maximum allows by the enOcean protocol. Real sensor will typically have much less switches.

I then added a member variable to store actuators status

 

    private boolean on[];

 

which is initialized in the constructor

 

    public RemoteDeviceEEPD20100(final ESP3Connector conn,

                                 final EnOceanId addressRemote,

                                 final EnOceanId addressLocal) {

        super(conn, addressRemote, addressLocal);

        on = new boolean[MAX_OUTPUTS];

           }

Next, I implemented the access methods that get and set the status of the actuators

    private boolean isOn(int index) {

       if ((index < 0) || (index >= MAX_OUTPUTS))

             return false;

      

        return on[index];

    }

 

    private void setOn(final DeviceParameterUpdatedInitiation initiation, final IOChannel channel, final Boolean on) {

       int minIdx = getIndexOfIOChannel(IOChannel.OUTPUT_CHANNEL_00);

       int maxIdx = getIndexOfIOChannel(IOChannel.OUTPUT_CHANNEL_1D);

       int index = getIndexOfIOChannel(channel);

 

             if ((index < minIdx) || (index > maxIdx))

             return;

      

        final Boolean oldOn = this.on[index];

this.on[index] = on;

fireParameterChanged(getDeviceParameterSwitch(index), initiation, oldOn, on);

    }

 

I can now implement the getByParameter and setByParameter

    @Override

    public Object getByParameter(final DeviceParameter parameter) throws IllegalDeviceParameterException {

        switch (parameter) {

            case ENERGY_WS:

return getEnergy();

            case POWER_W:

return getPower();

            case SWITCH:

            return isOn(0);

default:

            int minIdx = getIndexOfDeviceParameter(DeviceParameter.SWITCH_0);

            int maxIdx = getIndexOfDeviceParameter(DeviceParameter.SWITCH_29);

            int index = getIndexOfDeviceParameter(parameter);

            if ((index >= minIdx) && (index < maxIdx))

                    return isOn(index-minIdx);

            

return super.getByParameter(parameter);

        }

    }

 

    @Override

    public void setByParameter(final DeviceParameter parameter, final Object value) throws IllegalDeviceParameterException {

        assert DeviceParameter.getSupportedClass(parameter).isAssignableFrom(value.getClass());

LOGGER.debug("Invoking RemoteDeviceEEPD20100::setByParameter {} {}", parameter, value);

        switch (parameter) {

            case SWITCH:

switchOnOff(DeviceParameterUpdatedInitiation.SET_PARAMETER, DeviceParameter.SWITCH_0, (Boolean) value);

break;

default:

            int minIdx = getIndexOfDeviceParameter(DeviceParameter.SWITCH_0);

            int maxIdx = getIndexOfDeviceParameter(DeviceParameter.SWITCH_29);

            int index = getIndexOfDeviceParameter(parameter);

            if ((index >= minIdx) && (index <= maxIdx))

                    switchOnOff(DeviceParameterUpdatedInitiation.SET_PARAMETER, parameter, (Boolean) value);

            else

                    super.setByParameter(parameter, value);

        }

    }

 

The setByParameter method call the switchOnOff method, that created the radio packet and sent it out. This method creates an instance of the UserDataEEPD201CMD01 class, which has the logic to create the stream of bytes to send over the air

    public void switchOnOff(final DeviceParameterUpdatedInitiation initiation, final DeviceParameter channel, final boolean on) {

       int index = getIndexOfDeviceParameter(channel) - getIndexOfDeviceParameter(DeviceParameter.SWITCH_0);

 

       LOGGER.warn("Invoking RemoteDeviceEEPD20100::switchOnOff {}, {}", channel, index);

       UserDataEEPD201CMD01 userData = new UserDataEEPD201CMD01();

userData.setDimValue(DimValue.SWITCH_TO_NEW_OUT_VALUE);

userData.setIOChannel(getIOChannel(index));

userData.setOutputValueOnOff(on);

send(userData);

setOn(initiation, getIOChannel(index), on);

    }

 

The last feature to implement is to handle the incoming radio packet. I overrode the parseRadioPacket method. This method is called by the Aleoncean binding whenever a radio packet is received. This is my implementation

    @Override

    public void parseRadioPacket(RadioPacket packet) {

        if (!packet.getSenderId().equals(getAddressRemote())) {

            LOGGER.warn("Got a package that sender ID does not fit (senderId={}, expected={}).",

                        packet.getSenderId(), getAddressRemote());

            return;

        }

 

        if (packet instanceof RadioPacketVLD) {

parseRadioPacketVLD((RadioPacketVLD) packet);

        } else if (packet instanceof RadioPacketUTE) {

            parseRadioPacketUTE((RadioPacketUTE) packet);

        } else {

            LOGGER.warn("Don't know how to handle radio choice 0x%02X.", packet.getChoice());

        }

    }

 

    private void parseRadioPacketVLD(final RadioPacketVLD packet) {

        final UserDataEEPD201 userData = UserDataEEPD201Factory.createFromUserDataRaw(packet.getUserDataRaw());

        if (userData instanceof UserDataEEPD201CMD01

            || userData instanceof UserDataEEPD201CMD02

            || userData instanceof UserDataEEPD201CMD03

            || userData instanceof UserDataEEPD201CMD05

            || userData instanceof UserDataEEPD201CMD06) {

            LOGGER.warn("This command (0x%02X) shoule be sent to an actuator... Skip it.", userData.getCmd());

        } else if (userData instanceof UserDataEEPD201CMD04) {

            LOGGER.warn("Actuator status response received");

            handleIncomingActuatorStatusResponse((UserDataEEPD201CMD04) userData);

        } else if (userData instanceof UserDataEEPD201CMD07) {

            LOGGER.warn("Actuator measurement response received");

handleIncomingActuatorMeasurementResponse((UserDataEEPD201CMD07) userData);

        } else {

            LOGGER.warn("Unexpected user data received (CMD=0x%02X).", userData.getCmd());

        }

}

 

    public void handleIncomingActuatorStatusResponse(final UserDataEEPD201CMD04 userData) {

        try {

handleIncomingOutputValue(userData.getIOChannel(), userData.getOutputValueOnOff());

        } catch (UserDataScaleValueException ex) {

            LOGGER.warn("Something went wrong on status response handling.", ex);

        }

    }

 

    public void handleIncomingOutputValue(IOChannel channel, boolean on) {

        LOGGER.warn("{} - Received new output value: {} for channel: {}", getAddressRemote(), on, channel);

setOn(DeviceParameterUpdatedInitiation.RADIO_PACKET, channel, on);

    }

 

The last step is to make the library aware of the new profile, we need to change the DeviceFactory class. First, let's add the string that will identify the new profile in the items definition file

    private static final String RD_A50802 = "RD_A5-08-02";

    private static final String RD_A52001 = "RD_A5-20-01";

    private static final String RD_D20100 = "RD_D2-01-00"; //New line

    private static final String RD_D20108 = "RD_D2-01-08";

    private static final String RD_F60201 = "RD_F6-02-01";

 

and add the profile to the map of known profiles

 

MAP.put(RD_A50401, RemoteDeviceEEPA50401.class);

MAP.put(RD_A50802, RemoteDeviceEEPA50802.class);

MAP.put(RD_A52001, RemoteDeviceEEPA52001.class);

MAP.put(RD_D20100, RemoteDeviceEEPD20100.class); //New line

MAP.put(RD_D20108, RemoteDeviceEEPD20108.class);

MAP.put(RD_F60201, RemoteDeviceEEPF60201.class);

 

That's all as far as implementation of the new profile is concerned. Now we can build the library by running the command "mvn" from the directory where the Aleoncean source code has been unzipped (this is the directory that contains the pom.xml file, in this tutorial is "C:\Documents and Settings\Administrator\aleoncean-master").

If everything is ok, the new library will be created in the "target" folder.

Copy the file "aleoncean-0.0.1-SNAPSHOT-jar-with-dependencies.jar" in the folder "C:\Documents and Settings\Administrator\OpenHAB\org.openhab.binding.aleoncean\lib".

Build the OpenHAB runtime to include the changes to the Aleoncean library.

 

In next post I will try to send command to my Raspberry board.. stay tuned! For the moment, I attached the complete source code for the Aleoncean library, Aleoncean binding and OpenHAB (the latter has only the differences with the git version, so first download and install OpenHAB source from official git repository, then apply these patches)

Attachments:
aleoncean-master.zip
OpenHAB.zip
  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube