The Chain of Command

In order to make the kindle interacts with your home automation system, you have to :

  1. guess what both globally and precisely user is going to do
  2. imagine a way to transmit user's precise choice to an underlying piece of software
  3. trigger command when user's precise choice corresponds to a home automation instruction.

Obviously, Kindle already has a web navigator which could be used to command your home automation.


Obviously it's not what I'm aiming.

I want Kindle interacts - as a controller or as a sensor - with home automation smoothly, discretely.


Detection of user movement

Prepare the kindle

For the moment Kindle must be jailbreaked, the easiest way to do what we have to is through wifi and ssh. You can also use usb network and ssh, but keep in mind the Kindle have to be connected to your home automation a priori via wifi.

Getting kindle status

Kindle underlying system is a Linux distribution. You can see by yourselves that id, uname, df, dh, cp and bash are available.

The first think to do is looking inside logs. dmesg gives a lot of interesting stuff but nothing seems to help us guessing what user is actually doing.

If dmesg is our first shot, the second shot is spying in /var/log. So we run tail -f /var/log/* and observe this :

[root@kindle root]# tail -f /var/log/*
==> /var/log/udhcpc.log <==
Sending discover...
Offer from server xxx.xxx.1.1 received
Sending select for xxx.xxx.1.110...
Lease of xxx.xxx.1.110 obtained, lease time 43200

==> /var/log/wpa_supplicant <==

==> /var/log/wtmp <==
ttymxc0mxcLOGIN���U~~~reboot2.6.31-
(…) snip (…)
��U���0pts/0ts/0root192.168.1.203���U[T
==> /var/log/messages <==
150807:032805 powerd[2354]: I def:suspenddelta:sec=1890, cap=-1, mAh=-1, volt=-16, bp=-16:Suspend delta
150807:032805 syslog-ng[29521]: new configuration initialized

==> /var/log/messages <==
150807:032809 powerd[2354]: I lipc:evts:name=battLevelChanged, origin=com.lab126.powerd, fparam=59:Event sent

Still no usable information… But, we have not physically interact with kindle !

Observing user interactions

The command tail -f writes to your screen all the content of all files in /var/log in continue.

At this level I advise you to reset the terminal of ssh to only show what is new to logs file.

With your kindle in hand do regular things you always do. Like opening an ebook, a PDF, return to home screen, call parameters et cætera.

And now magic happened :

[root@kindle root]# tail -f /var/log/*
==> /var/log/messages <==
150807:034115 cvm[3363]: I FrameworkKeyEventDispatcher:USER_HARDKEY_PRESS:KeyPressed=Home:User pressed Home button
150807:034115 cvm[3363]: I BookletManager:SwitchingBooklets:from=Bookworm,to=Home:
150807:034115 cvm[3363]: I WebServicesImpl:UploadAnnotations::first journal empty
150807:034115 cvm[3363]: I Reader:SYNC LPR:position=374292:Send LPR to server
150807:034115 cvm[3363]: I HTMLBookViewer:Information::HTMLBookViewer closeBook save last read position 374292
150807:034115 webreader[3181]: I def:commandHandlerFunc:Received command: /command/close_uri, msg=0x298a58 :
150807:034115 webreader[3181]: I def:processCommandHandler:Response sent for this request, msg=0x298a58, time=1.863037 :
150807:034115 cvm[3363]: I HTMLBookViewer:Information::Request to close 9dabdc03-04c7-4ba7-b361-e64526ddd7f5 is processed successfully
150807:034115 cvm[3363]: I lipc:gsp:prop=orientation, source=com.lab126.system:Get string property
150807:034115 cvm[3363]: W SystemBarImpl:SPIN:spinCount=0:count <=0 when stopSpinner() called
150807:034115 cvm[3363]: I MainView:Information::The WiFi dialog has been previously shown.
150807:034116 cvm[3363]: I HTMLJsonSidecarFile:Information::Delayed Write to sidecar file
150807:034118 cvm[3363]: I GUIManager:HomeViewSwitch:view=CollectionView:
150807:034119 cvm[3363]: I FrameworkKeyEventDispatcher:USER_HARDKEY_PRESS:KeyPressed=Home:User pressed Home button
150807:034119 cvm[3363]: I BookletManager:SwitchingBooklets:from=Home,to=Home:
150807:034119 cvm[3363]: I GUIManager:HomeViewSwitch:view=HomeView:
150807:034119 cvm[3363]: W SystemBarImpl:SPIN:spinCount=0:count <=0 when stopSpinner() called
150807:034119 cvm[3363]: I MainView:Information::The WiFi dialog has been previously shown.
150807:034121 cvm[3363]: I FrameworkKeyEventDispatcher:USER_HARDKEY_PRESS:KeyPressed=Menu:User pressed Menu button
150807:034121 cvm[3363]: I lipc:gip:prop=userstoreTotalSpace, source=com.lab126.volumd:Get int property
150807:034121 cvm[3363]: I lipc:gip:prop=userstoreFreeSpace, source=com.lab126.volumd:Get int property
150807:034127 cvm[3363]: I MenuCursorHandler:USER_SOFTKEY_PRESS:KeyPressed=Paramètres:User selected the menu item Paramètres
150807:034127 cvm[3363]: I BookletManager:SwitchingBooklets:from=Home,to=Home:
150807:034127 cvm[3363]: I GUIManager:HomeViewSwitch:view=SettingsView:
150807:034127 cvm[3363]: W SystemBarImpl:SPIN:spinCount=0:count <=0 when stopSpinner() called
150807:034127 cvm[3363]: I SettingsView:WifiScanRequest:reason=scanForWifi() settings requested a scan:
150807:034127 cvm[3363]: I lipc:ssp:prop=scan, source=com.lab126.wifid:Set string property
150807:034127 wifid[2518]: I spectator:scan-start:t=59031.075438:
150807:034127 cvm[3363]: I LipcService:EventArrived:source=com.lab126.wifid,name=scanning,arg0=<None>,arg1=<None>:
150807:034127 wifid[2518]: I ipchandler:get_property:prop=currentEssid:
150807:034127 wifid[2518]: I ipchandler:set_property:prop=scan:
150807:034127 wifid[2518]: I wmgr:handleCtrlEvent:ev=Scan, state=Connected:
150807:034127 wifid[2518]: I sysev:dispatch:code=Scanning:
150807:034127 wifid[2518]: I spectator:scan-start:t=59031.075438:
150807:034127 wifid[2518]: I lipc:evts:name=scanning, origin=com.lab126.wifid:Event sent
150807:034128 wifid[2518]: I spectator:scan-complete:t=59032.189469:
150807:034128 cvm[3363]: I LipcService:EventArrived:source=com.lab126.wifid,name=scanComplete,arg0=<None>,arg1=<None>:
150807:034128 cvm[3363]: I SettingsView:Information::The Wi-Fi scanning task completed
What a mess ! But in fact not. Take the time to study, we can see what happened when we press home button from an ebook:
150807:034115 cvm[3363]: I FrameworkKeyEventDispatcher:USER_HARDKEY_PRESS:KeyPressed=Home:User pressed Home button
150807:034115 cvm[3363]: I BookletManager:SwitchingBooklets:from=Bookworm,to=Home:
Or we pressed the menu button :
150807:034121 cvm[3363]: I FrameworkKeyEventDispatcher:USER_HARDKEY_PRESS:KeyPressed=Menu:User pressed Menu button
Ok, enough for now. We can know what globally user is doing !


Getting user's precise choice

This precise part of the project took me 3 entire days. I'm on holiday, so it's entire days minus naps, sunbaths, swimming pool, so 3 fractions of days. That's said it was the trickiest part and I'm particular proud of.

What we get from now ? asin (sort of internal serial number) of the ebook, button pressed ? None of them is precise, all of them give a large view of what user is doing.

You can imagine interactions when user open that or that book, or close that or that book, or user open that book and press that or that button. It's enough for a scenario like "if hour is > x and < y and user close a book then shutdown bedroom lights".

But how can we extract what user is *really* *precisely* doing ? In simple words, Is there a way you can make a particular word appears in the log ? In fact no. You can't.

But it's possible way to make chosen words to appear. if a word IS IN the kindle dictionary then we can see in the logs :

150807:040426 cvm[3363]: I SupplementarInfoBox:QUICK_LOOKUP:asin=9dabdc03-04c7-4ba7-b361-e64526ddd7f5,word=saisir:

Victory ! There is a way to make a chosen word to be displayed in log. Oh and there is the asin available too !

In short

In one line of a file in /var/log we have THE global parameter - asin of displayed book - and THE precise parameter - where the user is - as the cherry on the cake.

Icing on the cherry on the cake, software uses a delay before requesting SupplementarInfoBox so user has "time" to move to precise word without triggering an unwanted one. "Flawless Victory".

Caveat, you can't use "LIGHT ON BUREAU" as a precise word, because that sequence is not in the dictionary. Good news "BUREAU" and "bureau" are different, if one can light on the other can light off.


The plumbery

The In

On the first side of the pipe we know where and how to guess what user is globally and precisely about to do. Hypothesize user wants to light on the room "BUREAU" then he or she moves the cursor of the reader to the word "BUREAU".

The Out

On the other side of the pipe hypothesize we have a simple API that calling http://homeautomator:8080/?toggle=BUREAU, toggles the roof lamp of the bureau. As said in "In Short" you can also imagine a http://homeautomator:8080/?command=BUREAU to light on and http://homeautomator:8080/?command=bureau to light off, be imaginative !

and between ?

We have to connect to log on first side and connect to the intranet on the other side.


Building the pipe

Identifying log engine

With a simple ps ax | grep syslog, we can see this is a the very friendly syslog-ng ! Very nice news because syslog-ng has a very flexible mechanism of rules, filters and command.

You have to modify your /etc/syslog-ng/syslog-ng.conf with vi. I strongly recommend you to learn minimal use of vi.

[root@kindle root]# grep cvm /etc/syslog-ng/syslog-ng.conf 
destination cvm { program("/tmp/read_fifo" template("$MSG\n")); };
filter f_cvm { match("cvm"); };
## If you want all CVM messages to go into
## /var/log/cvm in addition to /var/log/messages,
## uncomment the following line
log { source(src); filter(f_cvm); destination(cvm); };

I manually inserted the whole comment about CVM messages (lines start with ##).

What theses lines do ?

The first instructs syslog-ng that the line log have to be sent to /tmp/read_fifo (I will explain this file later !) and the template of the log must be reduced at it's minimum.
Intuitively, you feels the less we transmit, the less we have to parse, the less the time we spend, the less we consume power. So be it : template = $MSG.

The second instructs syslog-ng to only consider if cvm is present on the log line.
Feel free to change the filter to be more restrictive. Keep in mind this is the value chosen by Kindle's engineers.

The last one, log is the default rule, also defined by Kindle's engineers but was inhibited by the # on the start of the line. If the two previous lines do nothing by thenselves, the third activates them.

Designing the middle tube

This is what I decided to do :

  1. read lines from sdtin (according to syslog-ng documentation)
  2. never quit before EOF occurs in stdin (according to syslog-ng documentation)
  3. filter by regexp "SupplementarInfoBox:QUICK_LOOKUP:asin=.*,word=[A-Z]+:"
  4. trigger http GET if the previous filter matches.

With which tool ? Ah, great question, be imaginative. What is the best way ?

In fact you can do what you want. With bash it takes me 15 min to achieve the pipe. Really.

I prefer to do a tiny C program in two-three files I've later compiled with a kindle toolchain for linux.

See the step by step !