Yep - I finally got a chance to look at this. Seems the rtmbot has changed a bit since I wrote that integration. I have updated my GIST so try it out now and see if you have any luck.
works like a charm now! (as I use the script on the same openHABian as OH2, I had to replace the IP-address of my OH2 in slackbot.py with “localhost”, otherwise I got connection refused errrors with the API).
Thanks a lot!
Do you use some actions or rules to send updates from openHAB to slack? e.g. I’d like to have a notification, if my son’s home from school, or the temperature of my heating storage tank is low, …
For that I use mqttwarn
- so I publish the message to an MQTT topic (e.g. /openhab/notification/info
) and have mqttwarn
configured to watch that topic and publish the message to various endpoints (one of which is a Slack channel).
mqttwarn
supports dozens of different services for sending messages to.
OK, MQTT is on my to do list to check. Thanks.
Hey Thomas,
it’s always nice to see an idea work out in the end!
Do you think you can rephrase your first posting as instructions rather than a question so we can move this thread over to the Tutorials & Examples category? That would be amazing!
ok. I can do that. But first, I have to ask @ben_jones12 two more Things!
for now I just used send
, which worked with Switches (ON/OFF) and Numbers (e.g. 0-100 for light dimmers) and Strings.
From your readme, I get two more Options: “update
” and “items
”. Can you please explain the difference between send
and update
? In my cases, they had the same effect.
Unfortunately the command “items” threw some Errors:
items [Light_EG_WoZi_LG4]
2016-12-13 21:12:47,573 got process_message
2016-12-13 21:12:47,579 Starting new HTTP connection (1): localhost
2016-12-13 21:12:47,714 "GET /rest/items HTTP/1.1" 200 None
2016-12-13 21:12:47,721 Problem in Plugin Class: openHABPlugin: process_message
{u'text': u'items [Light_EG_WoZi_LG4]', u'ts': u'1xxx.xxx', u'user': u'Uxxx', u'team': u'Txxx', u'type': u'message', u'channel': u'Dxxx'}
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/rtmbot/core.py", line 205, in do
func(data)
File "/home/pi/binderhaus/plugins/slackhab.py", line 82, in process_message
for item in xml.fromstring(r.content).findall('item'):
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1300, in XML
parser.feed(text)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1642, in feed
self._raiseerror(v)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
raise err
ParseError: syntax error: line 1, column 0
I guess, it should write out some items and their states?
http://localhost:8080/rest/items works for me…
Thanks!
FYI - I have just made some changes to my script to make it easier to config and use.
Hi @ben_jones12,
I changed my config as described:
- added slackhab.ini
- changed slackhab.py
unfortunately I get this Errors, did I miss something?
2016-12-17 12:59:27,774 got process_user_typing
2016-12-17 12:59:27,877 got process_message
2016-12-17 12:59:27,892 Starting new HTTP connection (1): localhost
2016-12-17 12:59:28,026 "GET /rest/items HTTP/1.1" 200 None
2016-12-17 12:59:28,039 Problem in Plugin Class: SlackhabPlugin: process_message
{u'text': u'status Binder_Vacation', u'ts': u'1xxx.000010', u'user': u'U3CMCHKJN', u'team': u'Txxx', u'type': u'message', u'channel': u'Dxxx'}
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/rtmbot/core.py", line 205, in do
func(data)
File "/home/pi/binderhaus/plugins/slackhab.py", line 81, in process_message
item = self.get_single_item(filter, channel)
File "/home/pi/binderhaus/plugins/slackhab.py", line 112, in get_single_item
items = self.get_items(filter, channel)
File "/home/pi/binderhaus/plugins/slackhab.py", line 137, in get_items
for item in xml.fromstring(r.content).findall('item'):
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1300, in XML
parser.feed(text)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1642, in feed
self._raiseerror(v)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
raise err
ParseError: syntax error: line 1, column 0
2016-12-17 12:59:28,150 got process_desktop_notification
2016-12-17 12:59:31,274 got process_pong
2016-12-17 12:59:32,081 got process_user_typing
2016-12-17 12:59:32,184 got process_message
2016-12-17 12:59:32,191 Starting new HTTP connection (1): localhost
2016-12-17 12:59:32,353 "GET /rest/items HTTP/1.1" 200 None
2016-12-17 12:59:32,361 Problem in Plugin Class: SlackhabPlugin: process_message
{u'text': u'send Binder_Vacation OFF', u'ts': u'1xxx.000011', u'user': u'Uxxx', u'team': u'Txxx', u'type': u'message', u'channel': u'Dxxx'}
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/rtmbot/core.py", line 205, in do
func(data)
File "/home/pi/binderhaus/plugins/slackhab.py", line 54, in process_message
item = self.get_single_item(filter, channel)
File "/home/pi/binderhaus/plugins/slackhab.py", line 112, in get_single_item
items = self.get_items(filter, channel)
File "/home/pi/binderhaus/plugins/slackhab.py", line 137, in get_items
for item in xml.fromstring(r.content).findall('item'):
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1300, in XML
parser.feed(text)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1642, in feed
self._raiseerror(v)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
raise err
ParseError: syntax error: line 1, column 0
2016-12-17 12:59:32,468 got process_desktop_notification
2016-12-17 12:59:35,284 got process_pong
items
Switch Binder_Vacation "Urlaub" (gBinder)
rtmbot.conf:
# Add the following to rtmbot.conf
DEBUG: True # make this False in production
SLACK_TOKEN: "xoxb-114746204098-xxx"
ACTIVE_PLUGINS:
# - plugins.repeat.RepeatPlugin
- plugins.slackhab.SlackhabPlugin
./plugins/slackhab.ini:
[slackhab]
debug = True
openhab_url = http://localhost:8080
slackhab_user_id = Uxxx
./plugins/slackhab.py (copied from your gist):
import ConfigParser
import os
import requests
import time
import xml.etree.ElementTree as xml
from rtmbot.core import Plugin
SCRIPTDIR = os.path.dirname(__file__)
SCRIPTNAME = os.path.splitext(os.path.basename(__file__))[0]
CONFIGFILE = os.getenv(SCRIPTNAME.upper() + 'INI', os.path.join(SCRIPTDIR, SCRIPTNAME + '.ini'))
# parse our config/ini file
config = ConfigParser.ConfigParser()
config.read(CONFIGFILE)
debug = config.get('slackhab', 'debug')
openhab_url = config.get('slackhab', 'openhab_url')
slackhab_user_id = config.get('slackhab', 'slackhab_user_id')
# headers required for openhab REST API requests
headers = { 'Content-Type': 'text/plain' }
class SlackhabPlugin(Plugin):
def process_message(self, data):
print_debug("rx'd message: %s" % (str(data)))
# check we have sufficient details
if 'channel' not in data or 'user' not in data or 'text' not in data:
return
channel = data['channel']
user = data['user']
text = data['text']
# first check if we are interested in this command
command_text = get_command_text(channel, user, text)
if command_text is None:
return
tokens = command_text.split()
if len(tokens) == 0:
return
command = tokens[0].lower()
print_debug("command: %s" % (command))
if command == "send" and len(tokens) >= 3:
filter = tokens[1]
value = " ".join(tokens[2:])
item = self.get_single_item(filter, channel)
if item is None:
return
url = openhab_url + '/rest/items/' + get_item_attr(item, 'name')
r = requests.post(url, headers=headers, data=normalise_value(value))
if self.check_response(r, channel):
self.outputs.append([ channel, "```Sent %s command to %s```" % (value, get_item_attr(item, 'name')) ])
elif command == "update" and len(tokens) >= 3:
filter = tokens[1]
value = " ".join(tokens[2:])
item = self.get_single_item(filter, channel)
if item is None:
return
url = openhab_url + '/rest/items/' + get_item_attr(item, 'name') + '/state'
r = requests.put(url, headers=headers, data=normalise_value(value))
if self.check_response(r, channel):
self.outputs.append([ channel, "```Sent %s update to %s```" % (value, get_item_attr(item, 'name')) ])
elif command == "status" and len(tokens) >= 2:
filter = tokens[1]
item = self.get_single_item(filter, channel)
if item is None:
return
url = openhab_url + '/rest/items/' + get_item_attr(item, 'name') + '/state'
r = requests.get(url, headers=headers)
if self.check_response(r, channel):
self.outputs.append([ channel, "```%s is %s```" % (get_item_attr(item, 'name'), r.text) ])
elif command == "items":
filter = None
if len(tokens) > 1:
filter = tokens[1]
output = ""
maxtypelen = 0
maxnamelen = 0
items = self.get_items(filter, channel)
print_debug("%d items match %s" % (len(items), filter))
if len(items) == 0:
if filter is None:
self.outputs.append([ channel, "```No items found```" ])
else:
self.outputs.append([ channel, "```No items found matching '%s'```" % (filter) ])
else:
self.print_items(items, channel)
def get_single_item(self, filter, channel):
items = self.get_items(filter, channel)
items_count = len(items)
if items_count == 1:
print_debug("found single matching item: %s" % (get_item_attr(items[0], 'name')))
return items[0]
if items_count > 1:
self.outputs.append([ channel, "```Found %d items matching '%s', please restrict your filter```" % (items_count, filter) ])
self.print_items(items, channel)
else:
self.outputs.append([ channel, "```No item found matching '%s'```" % (filter) ])
return None
def get_items(self, filter, channel):
# cache this maybe?
url = openhab_url + '/rest/items'
r = requests.get(url, headers=headers)
if not self.check_response(r, channel):
return []
filter = filter.lower()
items = []
for item in xml.fromstring(r.content).findall('item'):
name = get_item_attr(item, 'name').lower()
if filter is None or filter in name:
items.append(item)
return items
def print_items(self, items, channel):
output = ""
maxtypelen = 0
maxnamelen = 0
# get the max name and type lengths so we can format our output nicely
for item in items:
name = get_item_attr(item, 'name')
type = get_item_attr(item, 'type')
if len(type) > maxtypelen:
maxtypelen = len(type)
if len(name) > maxnamelen:
maxnamelen = len(name)
for item in items:
name = get_item_attr(item, 'name')
state = get_item_attr(item, 'state')
type = get_item_attr(item, 'type')
output = output + "%s%s%s\n" % ( type.ljust( maxtypelen + 5 ), name.ljust( maxnamelen + 5 ), state )
if len(output) >= 8000:
output = output + "... (too much output, please specify a filter)"
break
self.outputs.append([ channel, "```%s```" % (output) ])
def check_response(self, r, channel):
# check our rest api call was successful
if r.status_code == 200:
return True
if r.status_code == 201:
return True
# log the response code/reason back to our slack channel
self.outputs.append([ channel, "```%d: %s```" % (r.status_code, r.reason) ])
return False
def normalise_value(state):
if state.lower() == "on":
return "ON"
if state.lower() == "off":
return "OFF"
if state.lower() == "open":
return "OPEN"
if state.lower() == "closed":
return "CLOSED"
return state
def get_command_text(channel, user, text):
if channel == "" or channel is None:
return None
if user == "" or user is None:
return None
if text == "" or text is None:
return None
# check for a message directed at our bot
user_tag = "<@%s>:" % (slackhab_user_id)
if text.startswith(user_tag):
return text[len(user_tag):]
user_tag = "<@%s>" % (slackhab_user_id)
if text.startswith(user_tag):
return text[len(user_tag):]
# check for a DM to our bot
if channel.startswith("D"):
return text
return None
def get_item_names(items):
return ", ".join(get_item_attr(item, 'name') for item in items)
def get_item_attr(item, attr):
return item.find(attr).text
def print_debug(message):
if debug:
print message
curl http://localhost:8080/rest/items
in the console brings up the item:
[(...){"link":"http://localhost:8080/rest/items/Binder_Vacation","state":"OFF","type":"Switch","name":"Binder_Vacation","label":"Urlaub","tags":[],"groupNames":["gBinder"]} (...)]
What do you see if you put http://<openhab-ip>:8080/rest/items
in your browser? Looks like the XML parser is having trouble with the XML returned.
Hi Ben. I posted it above. Of course there are more items than this. But does “send” need this, too?
That is JSON, not XML. My script is expecting XML. I didn’t realise OH2 had changed the REST API format to JSON - sorry!!
You will need to update my script to parse the JSON (pretty easy in Python with some Googling).
I added a diff to your gist here that makes your excellent Slack integration work under openHAB 2 (parse JSON and not XML). The wiki still needs a bit of an update, though.
Awesome - thanks John - I will be sure to pick this up when I upgrade to OH2!!
Ben can you share you mqttwarn.ini ? My config won’t post to slack.
item file
Switch Wohnzimmer { channel="hue:0100:xxxxxxxxxxx:1:brightness" }
String VT_Notify_Trace { mqtt=">[broker:/openhab/notification/trace:state:*:default]" }
String VT_Notify_Info { mqtt=">[broker:/openhab/notification/info:state:*:default]" }
String VT_Notify_Alert { mqtt=">[broker:/openhab/notification/alert:state:*:default]" }
String VT_Notify_Warn { mqtt=">[broker:/openhab/notification/warn:state:*:default]" }
String VT_Notify_Alarm { mqtt=">[broker:/openhab/notification/alarm:state:*:default]" }
rule file
rule "Wohnzimmer Licht"
when
Item Wohnzimmer changed
then
VT_Notify_Alarm.postUpdate("The burglar alarm has been tripped!!")
end
mqttwarn.ini
[config:slack]
token = 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx'
targets = {
# #channel/@user username, icon
'general' : [ '#allgemein', "openhab", ':syringe:' ],
}
[/openhab/notification/]
targets = slack:mqtt-client
format = -->{name}<--
Got it working with this config on mqttwarn.ini
Dont forget to add “slack” on launch inside mqttwarn.ini !
[config:slack]
token = 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx'
targets = {
# #channel/@user username, icon
'mqtt-client' : [ '#allgemein', "openhab", ':hankey:' ]
}
[/openhab/notification/]
targets = file:mylog, log:info
[#]
targets = slack:mqtt-client
FYI - I have updated this to use the new Slack Python SDK and dockerised it - see https://hub.docker.com/repository/docker/sumnerboy12/slackhab.
Thank you, Ben. I’ve been running your dockerised version since you shared it and it’s as great as it ever was. It stops occasionally for some odd reason but restarts no problem. I also switched to the dockerised mqttwarn and aside from needing to add the slack library to the image, also working great. Cheers! John
Good to hear! I have also switched to dockerised mqttwarn. Thanks for the feedback.