"Hacked" HABApp for using ROS

Hi,

I “hacked” HABApp for using the Robot Operating System (ROS). The problem is that rospy (library for ROS Python) only can initialize a node in a main thread and the HABApp Rules are not called by a main thread.

I tested it with Ubuntu 20.04, ROS Noetic Ninjemys and openHAB 3.

The result

/bin/bash -c 'source /etc/environment; /usr/bin/python3 /opt/habapp/bin/habapp -c /etc/openhab/habapp'
    __  _____    ____  ___
   / / / /   |  / __ )/   |  ____  ____
  / /_/ / /| | / __  / /| | / __ \/ __ \
 / __  / ___ |/ /_/ / ___ |/ /_/ / /_/ /
/_/ /_/_/  |_/_____/_/  |_/ .___/ .___/
                         /_/   /_/
                                     0.31.2
Error loading logging config: Unable to configure handler 'EventFile'
[INFO] [1650463466.290859]: Published ROS topic /openhab/state/Light_GF_Corridor_Ceiling with ON
[INFO] [1650463467.410154]: Published ROS topic /openhab/state/Light_GF_Corridor_Ceiling with OFF
[INFO] [1650463477.485426]: Published ROS topic /openhab/state/Light_GF_Bathroom with ON
[INFO] [1650463478.755158]: Published ROS topic /openhab/state/Light_GF_Bathroom with OFF
^CShutting down ...

Changes of HABApp

I edited /opt/habapp/bin/habapp to:

#!/opt/habapp/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
import rospy
from HABApp.__main__ import main
if __name__ == '__main__':
    rospy.init_node("habapp", anonymous=False)
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

So after starting HABApp I can see the node:

rosnode list
/habapp
/rosout

As you can see I started habapp with /usr/bin/python3 and not with python inside the venv. This is because I installed ROS not inside that virtual environment. Then I added habapp to my $PYTHOPATH:

export PYTHONPATH=$PYTHONPATH:/opt/habapp/bin:/opt/habapp/lib/python3.8/site-packages

Now I can run habapp outside of the virtual environment. And off course I run roscore before starting HABApp.

Note

If you want to use other Python libraries you can use the same workaround for using the $PYTHONPATH. ROS here was just an example of a special case because the nodes cannot be created outside of the main thread!

HABApp Rule for publishing ROS topics

Last but least I create following rule: (I called it openhab_bridge.py by the way)

import logging
import time
from typing import List
import HABApp
from HABApp import Parameter
from HABApp.core.events import ValueChangeEvent, ValueUpdateEvent
from HABApp.mqtt.items import MqttItem
from HABApp.openhab.events import ItemCommandEvent, ItemStateEvent
from HABApp.openhab.items import OpenhabItem
import rospy
from std_msgs.msg import String
import multiprocessing

log = logging.getLogger('openhab_bridge')
log_state = Parameter('openhab_bridge', 'log_state', default_value=True).value


class OpenHABBridge(HABApp.Rule):
    def __init__(self):
        super().__init__()

        for item in self.get_items(type=OpenhabItem):
            item.listen_event(self.on_item_state, ItemStateEvent)

    def node_init(self):
        rospy.init_node("openhab", anonymous=False)

    def on_item_state(self, event: ItemStateEvent):
        item = event.name
        value = event.value

        log.info(f'Published ROS topic /openhab/state/{item} with {value}')
        pub = rospy.Publisher(f'/openhab/state/{item}', String, queue_size=1)

        counter = 0

        while counter < 1:
            # wait for a connection to publisher
            # you can do whatever you like here or simply do nothing
            #rospy.loginfo("Published ROS topic /openhab/state/%s with value %>
            rospy.loginfo(f'Published ROS topic /openhab/state/{item} with {value}')
            pub.publish(str(value))
            counter = counter + 1


class LogItemStateRule(HABApp.Rule):
    """This rule logs the item state in the mqtt event bus log file"""

    def __init__(self):
        super().__init__()

        for item in self.get_items(type=OpenhabItem):
            item.listen_event(self.on_item_change, ValueChangeEvent)

    def on_item_change(self, event):
        assert isinstance(event, ValueChangeEvent)
        log.info(f'{event.name} changed from {event.old_value} to {event.value}')




OpenHABBridge()

# Create logger rule only if configured
if log_state:
    LogItemStateRule()

Please notice two things: You can see I have enabled logging. The configuration is not important if you want to test it. The second one is that I use a counter which should make sure that it will be only published once. So every change will be again published once.

After that I can show my topic list with rostopic list:

rostopic list
/openhab/state/Light_GF_Bathroom
/rosout
/rosout_agg

And I can also subscribe to it via command line with rostopic echo /openhab/state/Light_GF_Bathroom:

data: "ON"
---
data: "OFF"
---
data: "ON"
---
data: "OFF"
---

With this few changes you could be able to use openHAB and ROS together.

The installation guide for ROS Noetic can be found here:

http://wiki.ros.org/noetic/Installation/Ubuntu

If you are new to ROS you can learn it by doing the Tutorials:

http://wiki.ros.org/ROS/Tutorials

I think you used the wrong tag HABPanel Examples
Otherwise it’s always nice to see solutions with HABApp!