From i3Detroit
Jump to: navigation, search

Name mcclellan
Zone Infrastructure

Owner i3Detroit
Make Model Debian VM
Part Number
Date Acquired 2015-08-09
Storage Location hardac.i3detroit.local
Authorization Required Yes
Status Running
Value 0
IP Address
MAC Address 08:00:27:7e:08:1e
Hostname mcclellan
Documentation OpenHAB


Other References Supersedes:


This VM serves space automation functions. Currently, discussion is about two applications:

  • Smarter thermal control, with furnaces, AC, and fans controlled synchronously
  • Better power reporting from the Power Meter



Maintenance Info



  • IoT Subnet
* Wifi IoT network connected to IoT DHCP
* Connect mccllellan to IoT dhcp
* mysql already setup(including `mysql_secure_installation`), those two modules enabled, and config just commented out.
* include dust sensors
* add thermostat
* make them optional to keep compiled size down, and get them mainlined
  • try connecting multiple openhab instances across the internet
* mosquitto running on aws
* openhab cloud running on aws
* openhab instances at remote locations (our houses)

Authorized Users and Trainers

Trainer Name Certified Date
Amelia Meyer 2016-11-25
Evan Allen 2016-11-25
Leonard Kinnaird-Heether 2016-11-25
Mark Furland 2016-11-27
User Name Authorized By Date of Most Recent Training
Amelia Meyer Amelia Meyer 2016-11-25
Evan Allen Amelia Meyer 2016-11-25
Leonard Kinnaird-Heether Evan Allen 2016-11-25
Mark Furland Amelia Meyer 2016-11-27

Mcclellan Zone: Infrastructure "/>


OpenHAB2 runtime

Port 8080

To make config changes, there *might* be a openahb console command, but it didn't work. Restart the daemon.

To restart the daemon: `sudo service openhab2 restart`

To connect to the openhab 2 console: `ssh openhab@localhost -p 8101` password `habopen`. Those are the defaults. Not accessible from non-localhost.

Mosquitto MQTT broker

  • Port 1883

Listen to all: `mosquitto_sub -v -h localhost -p 1883 -t '#'`

Services that have yet to be reinstalled

Slack integration

Modified to work with new Slack RTM API
from __future__ import print_function
from __future__ import unicode_literals
import requests
import time
import xml.etree.ElementTree as xml
from rtmbot.core import Plugin

crontable = []
outputs = []

debug            = True

openhab_url      = ''
slackhab_user_id = "<<<elided>>>"

headers          = { 'Content-Type': 'text/plain' }

class SlackHab(Plugin):

    def process_message(self,data):

        # check we have sufficient details
        if 'channel' not in data or 'user' not in data or 'text' not in data:

        channel = data['channel']
        user = data['user']
        text = data['text']

        # first check if we are interested in this command
        command_text = self.get_command_text(channel, user, text)

        if command_text is None:
            self.print_debug('Boring message')

        tokens = command_text.split()
        if len(tokens) == 0:
            self.print_debug('No tokens')

        command = tokens[0].lower()

        if command == "send" and len(tokens) >= 3:
            item = tokens[1]
            value = " ".join(tokens[2:])
            url = openhab_url + '/rest/items/' + item
            r =, headers=headers, data=value)

            if self.check_response(r, channel):
                self.outputs.append([ channel, "```Sent %s command to %s```" % (value, item) ])

        elif command == "update" and len(tokens) >= 3:
            item = tokens[1]
            value = " ".join(tokens[2:])
            url = openhab_url + '/rest/items/' + item + '/state'
            r = requests.put(url, headers=headers, data=value)

            if self.check_response(r, channel):
               self.outputs.append([ channel, "```Sent %s update to %s```" % (value, item) ])

        elif command == "status" and len(tokens) >= 2:
            item = tokens[1]
            url = openhab_url + '/rest/items/' + item + '/state'
            r = requests.get(url, headers=headers)

            if self.check_response(r, channel):
                self.outputs.append([ channel, "```%s is %s```" % (item, r.text) ])

        elif command == "items":
            filter = None
            if len(tokens) > 1:
                filter = tokens[1].lower()

            url = openhab_url + '/rest/items'
            r = requests.get(url, headers=headers)

            if self.check_response(r, channel):
                items = []
                output = ""
                maxtypelen = 0
                maxnamelen = 0

                for item in xml.fromstring(r.content).findall('item'):
                    name = item.find('name').text
                    type = item.find('type').text
                    if filter is None or filter in name.lower():
                        if len(type) > maxtypelen:
                            maxtypelen = len(type)
                        if len(name) > maxnamelen:
                            maxnamelen = len(name)

                for item in items:
                    name  = item.find('name').text
                    state = item.find('state').text
                    type  = item.find('type').text
                    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)"

                if len(items) == 0:
                    if filter is None:
                        self.outputs.append([ channel, "```No items found```" ])
                        self.outputs.append([ channel, "```No items found matching '%s'```" % (filter) ])
                    self.outputs.append([ channel, "```%s```" % (output) ])

    def get_command_text(self,channel, user, text):
        if channel == "" or channel is None:
            self.print_debug('No channel')
            return None
        if user == "" or user is None:
            self.print_debug('No user')
            return None
        if text == "" or text is None:
            self.print_debug('No text')
            return None

        # check for a message directed at our bot
        user_tag = "<@%s>" % (slackhab_user_id)
        self.print_debug('Calculated user tag as %s'%user_tag)

        if text.startswith(user_tag):
            self.print_debug('Returning text from command in channel')
            return text[len(user_tag):]

        # check for a DM to our bot
        if channel.startswith("D"):
            self.print_debug('Returning text from command in DM')
            return text
        self.print_debug('No DM or channel command')
        return None

    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 print_debug(self,message):
        if debug:
DEBUG: False
SLACK_TOKEN: "<<<elided>>>"
BASE_PATH: "/var/lib/openhab/rtmbot"
    - plugins.slackhab.SlackHab
Systemd service
Description=OpenHAB Slack Bot

ExecStart=/usr/local/bin/rtmbot -c /var/lib/openhab/rtmbot/rtmbot.conf