Sunday 16 April 2017

HOME AUTOMATION: Teptron Move control from OpenHAB

I've previously explored how to control the bluetooth version of Teptron Move from a linux PC (or Raspberry Pi) with the built in Bluetooth adapter. While it's interesting to be able to do this from the Linux commands line it's not particularly practical for use in a home automation system. In this post

I'll explain how to integrate those controls with openHAB for more practical use.


OpenHAB Binding Configuration

In order to run a linux script from within openHAB we need to first install the "Exec Binding".

Exec Binding Installation


There are a few examples on how to configure the exec binding but none of the examples worked for me. I ended up following this post to get the right combination of Things/Items/Sitemaps working to give the following result:


I'll go in to some more detail below on how I configured my Things/Items/Sitemap to get the units working.


There are a few peculiarities in working with the Bluetooth version of the Teptron Move with the command line functions. I've noticed the following inconsistencies:

  • Sometimes the device does not respond and the command reports an error
  • Other times the device responds and accepts the commands but does not move
  • The time between receiving the accepted response and the device moving varies from immediate to ~5 seconds

I've tried a number of workarounds for this but so far I've had the most success with simply just retrying the command multiple times, but this has the issue of pausing the unit motion when the second/third command is resent. I'm not ready to give up on this one just yet, but I'm fast running out of ideas.


Move Control Script

Create a new script in the in the openHAB scripts folder:
 sudo nano /etc/openhab/scripts/teptron_move_control.sh  

Copy the following lines in to the file and save the file:
 #!/bin/sh  
 RESPONSE=""  
   
 # execute the command multiple times  
 #for i in 1 2 3  
 for i in 1  
 do  
  RESPONSE="$(/home/openhabian/development/csrmesh/bin/csrmesh-cli move --pin 8888 --dest 43:c5:5b:04:00:06 --objid $1 --position $2)"  
  echo "Response: ${RESPONSE}"  
  sleep 1  
 done  
   

Now make the script executable with the command:
 chmod +x /etc/openhab/scripts/teptron_move_command.sh  

This script accepts two input arguments:
  1. The move unit we want to move
  2. The position to be moved to (0 to 255)
Test the command is working as expected, as an example use the command:
 /etc/openhab/scripts/teptron_move_command.sh 2 255  

This will command unit 2 to move to the fully opened position.

Things Configuration

Create a new .things file for the blinds:
 sudo nano /etc/openhab/things/blinds.things  

Add the following lines:
 Thing exec:command:Blinds_GF_Lounge_North "Blinds" [ command="bash /etc/openhab2/scripts/teptron_move_control.sh 1 %2$s", interval=0, autorun=true ]  
 Thing exec:command:Blinds_GF_Lounge_South "Blinds" [ command="bash /etc/openhab2/scripts/teptron_move_control.sh 2 %2$s", interval=0, autorun=true ]  

The important part of the command here is the  name of the Thing, in this case "Blinds_GF_Lounge_North" and "Blinds_GF_Lounge_South". These will need to match the name of the items we will create next.


Items Configuration

Next we must define items that will take the input from the sitemap and send it as a parameter to the exec binding and execute the script:

Create a new .items file:
 sudo nano /etc/openhab/items/blinds.items  

Add the following items:
 String Blinds_GF_Lounge_North_Command "Blinds" { channel="exec:command:Blinds_GF_Lounge_North:input", autoupdate="false" }  
 String Blinds_GF_Lounge_South_Command "Blinds" { channel="exec:command:Blinds_GF_Lounge_South:input", autoupdate="false" }  

Ensure the names of the defined items match the names of the things we defined previously.

Sitemap Configuration

We need to define a couple of switches with custom mapping to command the move units to the "Open" and "Close" positions:
 sudo nano /etc/openhab/sitemaps/default.sitemap  

Add the following lines where applicable:
 Switch item=Blinds_GF_Lounge_North_Command label="North Blinds" mappings=["255"="Open", "0"="Close"]  
 Switch item=Blinds_GF_Lounge_South_Command label="South Blinds" mappings=["255"="Open", "0"="Close"]  

If everything was working properly, pressing the buttons on the sitemap should command the move units to the open and closed positions.



Technically it should be possible to use a slider here to set the blind position, but I'm yet to work out how to do it.

Remember the following command is great to be able to debug any openHAB issues:
 tail -f /var/log/openhab2/openhab.log -f /var/log/openhab2/events.log   

Saturday 8 April 2017

ROBOTICS: Android App for Bluetooth Remote Control Robots

A while back I was tinkering with the MIT App Inventor project and wrote a little android app that would connect to a bluetooth device and send ASCII commands via a bluetooth serial connection. It turned out to be a really handy way to control my robots over bluetooth so I shared it and made a short youtube video showing how to use it. It's my most successful youtube video to date and I still get people asking for the codes.

Since then I taught myself to code for Android and rewrote the app with plans of releasing it on the app store, but never found the motivation to take it all the way. It's been a couple of years since I put any time in to active development of the app, so in the spirit of sharing the codes I've decided to upload my project to Github for anyone who is interested. To make it easier for use I've also included a compiled apk.


The App

Basically, the app connects to a bluetooth device and creates a serial connection and streams ASCII characters to and from the connected device. Typically the way I use this by assigning specific ASCII characters to buttons, then when coding my android project I interpret the ASCII characters and react based on the command.
Vertical Layout

Landscape Layout

Pair with Bluetooth device

Preferences and Help menu


The app also allows you to change the button labels as well as the characters being sent for each button.

The app requires a minimum of Android 4.0 and has been written to support a number of screen resolutions and configurations.

Features

Inverted button layout switches the position of the direction keys and action keys:





Receive commands from your robot with the Feedback Group scale scale bars. Commands need to be sent over serial in the format "SB1=XX" where XX is a number from 0 to 100.






Known Bugs

Pairing a new bluetooth device is clunky. I've had the most success in performing the pairing from within the settings menu first, then going back in to the app and connecting to the bluetooth device.

Monday 3 April 2017

HOME AUTOMATION: ELK/NESS M1 Home Alarm Controller with OpenHAB over Serial

When I installed the NESS M1 home automation controller and home alarm I had plans of integrating it in to my overall setup. I purchased an M1XEP with plans of exposing the alarm control to the network but ran in to setup issues that I was never able to resolve.

When I installed my openHAB controller I looked for a binding for the NESS/ELK but was not able to find one. There are some active conversations on the forums but no available bindings, so I decided to try my hand at getting something going. I'm using a USB to Serial adaptor plugged from the serial port of my home alarm directly in to my Raspberry Pi running openHAB2.


OpenHAB Serial Binding

First thing to do is to install and configure the serial binding. I have already covered this in an older post.



Reading Zone States

The easiest thing to start with is to read the zone state from the NESS alarm controller. With serial configured this is simply a matter of reading the serial input data and interpreting the commands based on the well documented serial protocol for the controller.


Here are the files I have used to mirror the state of the motion sensors on my home alarm by reading the zone states:


alarm.items
 Switch AlarmUpdate  "Alarm Update"  { serial="/dev/ttyUSB0@115200" }  
 String AlarmMessage "Alarm Message" { serial="/dev/ttyUSB0@115200" }  
 Group gAlarm_PIR
 Contact Alarm_PIR_Zone1                                                (gAlarm_PIR)
 Contact Alarm_PIR_Zone2                                                (gAlarm_PIR)
 Contact Alarm_PIR_FF_MasterBedroom "Master Bedroom Motion Sensor [%s]" (gAlarm_PIR)
 Contact Alarm_PIR_Zone4                                                (gAlarm_PIR)
 Contact Alarm_PIR_Nursery "Nursery Motion Sensor [%s]"                 (gAlarm_PIR)
 Contact Alarm_PIR_FF_Stairs "Stairs Motion Sensor [%s]"                (gAlarm_PIR)
 Contact Alarm_PIR_GF_Lounge "Lounge Motion Sensor [%s]"                (gAlarm_PIR)
 Contact Alarm_PIR_GF_Stairs "Basement Stairs Motion Sensor [%s]"       (gAlarm_PIR)

init.rules
 rule "Init"  
 when  
     System started  
 then  
     // let openHAB settle and give other rules the chance to fire  
     createTimer(now.plusSeconds(180)) [|  
         gAlarm_PIR?.members.forEach[g |  
             g.postUpdate(CLOSED)  
         ]  
 end  


alarm.rules
 import java.lang.Integer.*  
 val AlarmMemoryMessage = "AM"  
 val ZoneChangedMessage = "ZC"  
 val NetworkAdapterPing = "XK"  
 val ZoneStatusTable_NormalUnconfigured = "0"  
 val ZoneStatusTable_NormalOpen = "1"  
 val ZoneStatusTable_NormalEOL = "2"  
 val ZoneStatusTable_NormalShort = "3"  
 val ZoneStatusTable_TroubleOpen = "5"  
 val ZoneStatusTable_TroubleEOL = "6"  
 val ZoneStatusTable_TroubleShort = "7"  
 val ZoneStatusTable_ViolatedOpen = "9"  
 val ZoneStatusTable_ViolatedEOL = "A"  
 val ZoneStatusTable_ViolatedShort = "B"  
 val ZoneStatusTable_BypassedOpen = "D"  
 val ZoneStatusTable_BypassedEOL = "E"  
 val ZoneStatusTable_BypassedShort = "F"  
 rule "Interpret Alarm Command"  
 when  
     Item AlarmMessage received update  
 then  
     var lines = AlarmMessage.state.toString.split('\n')  
     lines.forEach[line |  
         if(line.length > 6) {  
             var messageLength = line.substring(0,2)  
             var commandType = line.substring(2,4)  
             var checksum = line.substring(line.length-3, line.length-1)  
             //logInfo("alarm", "Message:" + line + " Len:" + messageLength + " Command:" + commandType)  
             if(commandType == AlarmMemoryMessage) {  
                 var alarmMemoryAreas = line.substring(4, 12)  
             }  
             else if(commandType == ZoneChangedMessage) {  
                 else if(commandType == ZoneChangedMessage) {  
                 var int zoneNumber = Integer::parseInt(line.substring(4,7))  
                 var zoneStatus = line.substring(7,8)  
                 //logInfo("alarm", "Zone:" + zoneNumber + " Status:" + zoneStatus)  
                 //logInfo("alarm", "gAlarm_PIR:" + gAlarm_PIR.members.filter(g | g.name == "Alarm_PIR_GF_Lounge").toString)  
                 var zone = Alarm_PIR_GF_Lounge  
                 var state = CLOSED  
                 // case statement setting the zone item from the message received  
                 switch zoneNumber {  
                     case zoneNumber == 1 : zone = Alarm_PIR_Zone1  
                     case zoneNumber == 2 : zone = Alarm_PIR_Zone2  
                     case zoneNumber == 3 : zone = Alarm_PIR_FF_MasterBedroom  
                     case zoneNumber == 4 : zone = Alarm_PIR_Zone4  
                     case zoneNumber == 5 : zone = Alarm_PIR_FF_Nursery  
                     case zoneNumber == 6 : zone = Alarm_PIR_FF_Stairs  
                     case zoneNumber == 7 : zone = Alarm_PIR_GF_Lounge  
                     case zoneNumber == 8 : zone = Alarm_PIR_GF_Stairs  
                 }  
                 // set the state of the zone from the message received  
                 if(zoneStatus == ZoneStatusTable_NormalUnconfigured) {  
                     state = CLOSED  
                 }  
                 else if(zoneStatus == ZoneStatusTable_NormalOpen) {  
                     state = CLOSED  
                 }  
                 else if(zoneStatus == ZoneStatusTable_NormalEOL) {  
                     state = CLOSED  
                 }  
                 else if(zoneStatus == ZoneStatusTable_TroubleOpen) {  
                     state = OPEN  
                 }  
                 else if(zoneStatus == ZoneStatusTable_TroubleEOL) {  
                     state = OPEN  
                 }  
                 else if(zoneStatus == ZoneStatusTable_TroubleShort) {  
                     state = OPEN  
                 }  
                 else if(zoneStatus == ZoneStatusTable_ViolatedOpen) {\  
                     state = OPEN  
                 }  
                 else if(zoneStatus == ZoneStatusTable_ViolatedEOL) {  
                     state = OPEN  
                 }  
                 else if(zoneStatus == ZoneStatusTable_ViolatedShort) {  
                     state = OPEN  
                 }  
                 else {  
                     state = CLOSED  
                 }  
                 postUpdate(zone, state)  
             }  
             else if(commandType == NetworkAdapterPing) {  
             }  
             else {  
                 //logInfo("alarm", "No code match")  
             }  
         }  
     ]  
 end  


Add the following lines to your sitemap:
 Frame label="Ground Floor Stairs" {  
   Text item=Alarm_PIR_GF_Stairs icon="motion"  
 }  


Testing

View the sitemap and we should see the motion sensor updates coming through



Test the sitemap is working as expected with the command:
 tail -f /var/log/openhab2/openhab.log -f /var/log/openhab2/events.log tail -f /var/log/openhab2/openhab.log -f /var/log/openhab2/events.log  

If everything is working as expected you should see updates coming through:
 ==> /var/log/openhab2/events.log <==  
 2017-04-03 13:44:24.818 [GroupItemStateChangedEvent] - gAlarm_PIR changed from CLOSED to UNDEF through Alarm_PIR_GF_Lounge  
 2017-04-03 13:44:28.191 [ItemStateChangedEvent   ] - AlarmMessage changed from 0AZC007900C2  
 1EAS000000000111111100000000000F  
 0CAM000000007F  
  to 0AZC007200C9  
 1EAS000000001111111100000000000E  
 0CAM000000007F  
 2017-04-03 13:46:26.579 [ItemStateChangedEvent   ] - Alarm_PIR_GF_Lounge changed from OPEN to CLOSED  


Update

There is a bug in openHAB2 that stops icon updates on the sitemap as the states are updated. It's documented here. The solution is to restart the OpenHAB server.