I have always been on the lookout for tracking devices which enable me to automate events in my house based on my location.
The obvious route I first took was to scan my network to check if my iphone is in range. Seemed a trivial solution that should work easily. I was in for a surprise. I found it works sometimes and is therefore not reliable and unusable. The problem lies in the fact that the iphone goes to sleep to save power. Also my solution was based upon polling my network to detect if my iphone had registered through DHCP. The delay of my iphone sleeping and the polling model means my automation script would trigger way after I had entered my house, not wat I was looking for.
What alternatives can I find:
- iphone wifi detect when in range of home (as described not very succesfull)
- bluetooth iBeacon
- iPhone gps tracking using owntracks
Both the iBeacon and the gps tracking using owntracks where somewhat succesfull but both do have some accuracy issues. Maybe this is a subject for another time.
Recently I came across an alternative: a mobile tracking device which is very easy to install. It is battery powered and can last 3 or 12 years depending on the model.
Track your car using openhab and Komma EYE!
The technology is based on a low power module that you hide in your car. There are no connections needed because the module is battery based. The module can be purchased at http://www.komma-trace.nl/. Send them en email, there is no shop on their site.
And the product has a supporting website at: http://trackeye.eu/Animation/en/index.html
But using a website to view the module location is not the preferred way to integrate with my home automation system.
I use an openhab setup. http://www.openhab.org/ I like to connect devices to openhab using a networking protocol. My preferred way to integrate at the moment is to use a messagebus like mqtt. So I decided to develop a small program to request the module location and post the information on mqtt. Openhab can then process the information and store the vehicle location. Scripts can then be run using the openhab scripting model. Openhab is pretty good at scripting but is often a lot of trouble to integrate special hardware. Using mqtt as a generic interface I get best of both worlds.
But how do I get the gps location? I logged into the supporting website and found is was developed as a single page application (SPA) in ASP.Net. SPA’s are difficult to screenscrape because they are not based on a simple HTTP POST model. I decided to contact the developer and was pleased to hear that they are working on an mqtt interface but this is not production ready yet. The alternative they currently have is a REST service. Excellent!
After some emails with Komma Trace I was provided with credentials to connect to the REST interface and receive the GPS location information.
Here is an example of the type of call that needs to be done:
http://flexws.appserver.dk/flexws/latestsutmessage/form/
The resulting data is JSON and can easily be processed using c# and .Net 4.0, Add a reference to System.Web.Extensions.
This code can run on mono and there ia a simple mqtt client on unix to send the data to openhab. But first the c# code:
using System; using System.Net; using System.IO; using System.Collections.Specialized; using System.Collections.Generic; using System.Linq; using System.Text; using System.Globalization; namespace GPSTrack { class Program { static void Main(string[] args) { // Please provide your device specific guid here. This can be requested from Komma Trace. KommaWeb loWeb = new KommaWeb("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx"); if (loWeb.DataReady) { Console.WriteLine(loWeb.ToJSON()); } else { Console.WriteLine(string.Format("Error: {0}", loWeb.LastAction)); } } } public class KommaWeb { public bool DataReady; public string LocationText; public string Temperature; public double Latitude; public double Longitude; public DateTime LastPosition; public DateTime LastUpdate; public string LastAction; public KommaWeb(string psGUID) { LastAction = "Init"; DataReady = false; LastAction = "Connect to site flexws.appserver.dk"; String lsBaseUrl = "http://flexws.appserver.dk"; HttpWebRequest loHTTPRequest = createRequest(lsBaseUrl + string.Format("/flexws/latestsutmessage/?guid={0}&tz=Europe%2FAmsterdam&mask=", psGUID)); LastAction = "And get data"; StringBuilder sbResponse; sbResponse = getResponseText(loHTTPRequest); LastAction = "parse JSON answer"; Gpsmessage result = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Gpsmessage>(sbResponse.ToString()); DataReady = true; Latitude = result.sutmessage.gpsinfo.lat; Longitude = result.sutmessage.gpsinfo.lon; LastPosition = result.sutmessage.ts_full; LastUpdate = LastPosition; Temperature = result.sutmessage.temp.ToString(); // Retrieving the location is another story... use google location services... LocationText = ""; } /// <summary> /// Summary in string format /// </summary> /// <returns></returns> public override string ToString() { string lsRegels = "KommaTrace" + System.Environment.NewLine; lsRegels += string.Format("Location: {0}", LocationText.ToString(CultureInfo.InvariantCulture)) + System.Environment.NewLine; lsRegels += string.Format("GPS: {0};{1}", Latitude.ToString(CultureInfo.InvariantCulture), Longitude) + System.Environment.NewLine; lsRegels += string.Format("LastUpdate: {0}", LastPosition.ToString(@"yyyy-MM-dd HH:mm:ss")) + System.Environment.NewLine; lsRegels += string.Format("Temperature: {0}", Temperature) + System.Environment.NewLine; return lsRegels; } /// <summary> /// Summary in string format, plain and simple.... /// </summary> /// <returns></returns> public string ToJSON() { string lsRegels = "{" + System.Environment.NewLine; lsRegels += @"""objectlocation"": {" + System.Environment.NewLine; lsRegels += string.Format(@"""description"": ""{0}"",", LocationText) + System.Environment.NewLine; lsRegels += string.Format(@"""temperature"": ""{0}"",", Temperature) + System.Environment.NewLine; //lsRegels += @"""gps"": {" + System.Environment.NewLine; lsRegels += string.Format(@"""latitude"": {0},", Latitude.ToString(CultureInfo.InvariantCulture)) + System.Environment.NewLine; lsRegels += string.Format(@"""longitude"": {0},", Longitude.ToString(CultureInfo.InvariantCulture)) + System.Environment.NewLine; //lsRegels += "}," + System.Environment.NewLine; // GPS lsRegels += string.Format(@"""lastupdate"": ""{0}""", LastPosition.ToString(@"yyyy-MM-dd HH:mm:ss")) + System.Environment.NewLine; lsRegels += "}" + System.Environment.NewLine; lsRegels += "}"; return lsRegels; } private HttpWebRequest createRequest(String psUrl) { HttpWebRequest loRequest = (HttpWebRequest) System.Net.WebRequest.Create(psUrl); loRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322)"; loRequest.CookieContainer = new CookieContainer(); // Expect100 does not behave well when using POST data // System.Net.ServicePointManager.Expect100Continue = false; loRequest.CachePolicy = new System.Net.Cache.RequestCachePolicy( System.Net.Cache.RequestCacheLevel.NoCacheNoStore); return loRequest; } private StringBuilder getResponseText(HttpWebRequest poRequest) { return getResponseText(poRequest.GetResponse()); } // Copy stream to stringbuilder... private StringBuilder getResponseText(WebResponse poResponse) { StringBuilder loData = new StringBuilder(); byte[] buf = new byte[8192]; Stream loResStream = poResponse.GetResponseStream(); int count = 0; while ((count = loResStream.Read(buf, 0, buf.Length)) > 0) { loData.Append(Encoding.ASCII.GetString(buf, 0, count)); } loResStream.Close(); return loData; } } // The structures are for storing the device data. public class Echotag { } public class Gpsinfo { public double lat { get; set; } public double lon { get; set; } public int speed { get; set; } public int ttf { get; set; } public int radius { get; set; } } public class Gsminfo { public string cellid { get; set; } public int rssi { get; set; } public string lac { get; set; } public string mcc { get; set; } public string mnc { get; set; } } public class Sutmessage { public int ts { get; set; } public string date { get; set; } public string time { get; set; } public string nodeid { get; set; } public int tno { get; set; } public string iccid { get; set; } public string us { get; set; } public int ta { get; set; } public int temp { get; set; } public int version { get; set; } public int fwversion { get; set; } public Echotag echotag { get; set; } public Gpsinfo gpsinfo { get; set; } public List<Gsminfo> gsminfo { get; set; } public DateTime ts_full { get { DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime(); return epoch.AddSeconds(ts); } } } public class Gpsmessage { public Sutmessage sutmessage { get; set; } } }
And how to we send this to Openhab? I use a python script because I was able to find an mqtt library for python.
import paho.mqtt.publish as publish import json from subprocess import check_output outcar = check_output(["/opt/local/bin/mono", "/var/regent/KommaTrace.exe"]) #print outcar data = json.loads(outcar) print json.dumps(data["objectlocation"]["description"], indent=4, separators=(',', ': ')) print json.dumps(data["objectlocation"]["temperature"], indent=4, separators=(',', ': ')) print json.dumps(data["objectlocation"]["longitude"], indent=4, separators=(',', ': ')) print json.dumps(data["objectlocation"]["latitude"], indent=4, separators=(',', ': ')) print json.dumps(data["objectlocation"]["lastupdate"], indent=4, separators=(',', ': ')) # provide your mqtt credentials here: loauth = {'username':"***username***", 'password':"***password***"} publish.single("kommatrace/car/location", json.dumps(data["objectlocation"]), hostname="openhabserver.local", auth=loauth, port=8883, retain=True, qos=0)
In openhab I have an item to receive mqtt messages:
String Car_location "Car" (GPS) {mqtt="<[openhabserver:kommatrace/car/location:state:default]"}
And some items to store the data:
Number Car_temp "Temp [%s]" (GPS) Number Car_latitude "Latitude [%s]" (Status) Number Car_longitude "Longitude [%s]" (Status) String Car_position "Car [%s]" (Status) String Car_lastupdate "Time [%s]" (Status)
And I have a rule to process the data:
rule "Car Position" when Item Car_location received update then logInfo("Car", "Start.") var String json = (Car_location.state as StringType).toString //{"latitude": 52.0*** "lastupdate": "2015-10-22 14:54:15", "longitude": 4.****, "description": ""} var String lat = transform("JSONPATH", "$.latitude", json) var String lon = transform("JSONPATH", "$.longitude", json) var String pos = transform("JSONPATH", "$.description", json) var String temp = transform("JSONPATH", "$.temperature", json) var String last = transform("JSONPATH", "$.lastupdate", json) postUpdate(Car_latitude, lat) postUpdate(Car_longitude, lon) postUpdate(Car_positie, pos) postUpdate(Car_temp, temp) postUpdate(Car_lastupdate, last) end
Happy coding!
Next time, how to plot location on map?