New Gmail Design

New Gmail Design

imageOkay, so I like the new Gmail design – cleaner and more whitespace – just looks much more modern that the existing one.

But it’s still bloody annoying !

The thing is, I hate having to go across to the little ‘back arrow’ reply icon, click the dropdown next to it, just to get to the ‘actions’ I can carry out on an email.

‘Forwarding’ is two clicks when I simply want a single click for it. I’m surprised no one has come up with a greasemonkey script that does this – maybe I need to stop whinging and write myself one.

Microsoft do a good job in the old Outlook Web Access (2003 and 2007) and in the newer Outlook Web App (2010)


What do you think ?

Drag and Drop from a TreeView to a DataGridView

imageAn application I was working on recently needed the ability for the user to drag a TreeView node onto a DataGridView, and when ‘dropped’ create a new row in the DataGridView.image

It is not difficult, but took me a while to put all the relevant pieces together, so I thought I’d post it in case someone else finds it useful.


There are four key aspects to it, summarized as :

  1. Call ‘DoDragDrop’ in the ‘ItemDrag’ event of the source control.
  2. Setting the ‘DragDropEffect’ in the ‘DragEnter’ event of the destination control
  3. Setting ‘AllowDrop’ to true on the destination control
  4. Handling the ‘DragDrop’ event on the destination control.

Inside the ‘ItemDrag’ event we call the DoDragDrop function, this initiates the whole drag drop feature. Setting the DragDropEffect in the DragEnter event on the destination control gives the user a visual clue as to whether it is a move, copy, shortcut etc, by changing the pointer to the correct icon.
Setting ‘AllowDrop’ to true enables the destination control to ‘accept’ the item being dropped onto it, and finally in the DragDrop event of the destination control we do the actual work (get the details of the item that was dragged and put it into the DataGridView in our case).

Another thing to consider is whether you just want to add the dragged item to the destination control, or whether you want to insert it at a particular location based on where the item was dropped.
To insert at a particular location, you’ll need to get the X and Y co-ords of the drop location, then translate this to the correct X and Y location with reference to the control itself, and from that get the current item that lives at that location on the control and use that as the reference for inserting the new item.

The code below shows the relevant bits :

void treeView1_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
    DoDragDrop(e.Item, DragDropEffects.Copy);

void dgvSteps_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
    e.Effect = DragDropEffects.Copy;

private void dgvSteps_DragDrop(object sender, DragEventArgs e)
    TreeNode treeNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
    if (treeNode.Tag == null)
        return; // ignore the category nodes

    // Find out where the drop occurred
    Point pnt = dgvSteps.PointToClient(new Point(e.X, e.Y));
    DataGridView.HitTestInfo info = dgvSteps.HitTest(pnt.X, pnt.Y);
    if (info.RowIndex == –1)
        // it was dropped on an empty area so add at end
        // it was dropped on an existing row, so insert before that row
        InsertNewRow((MyObj)treeView.Tag, info.RowIndex);

Hope someone finds this useful. Drop me a line in the comments with any questions, or if you found this useful.

GEO: 51.4043807983398 : -1.2872029542923


Android Development / Hundred Pushups App

android_logoThis is a bit of a mixed post, Eclipse / Android development versus Visual Studio / Windows development, and a bit of self promotion of my ‘Hundred Pushups’ app.

I have been a fan of the Hundred Pushup Challenge for some time, having done weeks 3 – 6 a number of times and completed the challenge a few times.
A few months ago while I was on the site I noticed there was an iPhone app for it but no Android app. I had been looking for excuse to build ‘something Android’ for a while and this looked like an ideal candidate (I am a massive Android fanboy !!).

The application requirements were not too complex, provide the 6 week, times 3 day, time 3 level schedule, a way of recording workouts and tests and some figuring out accelerated advancement of weeks based on test results. A bit of database back end, a bit of schedule / table UI and a couple of screens each for workouts and test.

I downloaded the Android SDK, tools and development environment (Eclipse) and got started. I use subversion for source control and there is a well integrated SVN plugin for Eclipse, so that was all fine. The first hurdle was the framework / API, but that is fairly common when looking at a new framework / toolset – and is simply down to experience. So, although things took longer than I expected, I have no complaints there – plenty of online examples on how to do various tasks.visual_studio_logo

The next hurdle was the UI – it has to be said that the UI tools for Android are nowhere near the sophistication of the Visual Studio experience. This is not down to developer experience, or understanding different methodologies – the UI design tools are just not there – there is no visual layout tool. I found this to be the most frustrating aspect – rework the xml layout definition, trial run, scratch head as to when certain elements were just not visible – rinse and repeat.

As a comparison, I just finished a client component for some back end technology I have been working on – this has treeview controls, three interlinked datagrids, toolbars and menus with buttons, dropdowns and dropdown buttons – this took around 3 days, compared to over two weeks with Android for a main ‘switchboard’ screen with button branching off to four ‘task’ screens – no complex UI elements al all.

The reason Microsoft and .NET are so ubiquitous is the barrier to entry is so low – they give the tools away for free (express SKUs) and the UI design / prototyping is so fast – I can have a complex desktop UI application up and running in hours, not days. Android has some way to go before it can match this, and , unfortunately, I feel that even though a Microsoft / Windows tablet may be technically inferior (i.e. more bloated), it will win in the end because the development tools are easier making applications more readily available / pervasive.

Gratuitous Self Promotion

So, unfortunately, when I got my Hundred Pushups app completed the guy responsible for Hundred Pushups had already agreed to give the digital rights to someone else. He asked me not to release my app in the Android Market, which I can respect. However, I do believe that (right now – April 2011) my app is better than the paid for version available from the people who have the digital rights. I have included some screenshots below. If you want to test this out then drop me a mail and I’ll left you have a copy…


GEO 51.4043388366699:-1.2875679731369

SportyPal Screen Scraping with PowerShell

imageFor the past year (2010) I had been using SportyPal – an application for tracking exercise (runs mainly for me). It has mobile apps (iPhone, Android, WinMob etc.) that do the actual tracking and then upload the data to their website where you can view history, graphs, charts, records etc. It really is neat, and the user interface on the mobile device really looks good (at least the WinMob and Android versions I used)…

I have been pinging them on their forums for a while now about when their subscription service would be launched and about getting a subscription trial – they announced that they expected it in Summer 2010, it got delayed and delayed and although I got vague answers about release dates from their forum and twitter responses, it still was not available by the end of December 2010.

imageSo, much as I loved their app, it was time to switch – RunKeeper was the new app/service I chose. The problem I faced was how to get my years worth of data out of SportyPal – they do allow you to export GPX data for each run, but it’s on a run by run, manual basis – not good.

Time to crack open PowerShell…

A bit of poking around with Chrome Developer Tools and Fiddler2 identified the sequence for logging in and the format of the history/activity page that lists all runs.
So now I had a basis for screen-scraping the data I needed, and I also notice that for each run there was a link to download the GPX.

I put together a script that would login, open the activities page and grab the data about each run, it also downloads a copy of the GPS data to a separate file for each run, with the filename set as the date/time stamp of the run.
I couldn’t see any easy method of importing (but to be honest I didn’t look for very long), but as I only had 100 runs to import I simply did it manually (RunKeepers import function is only about 3 clicks).

Initially I found that every GPX file I imported came up with an error about the GPX being invalid, however, after a browse around the forums I found that one of the namespaces was incorrect ( when it should be This did the trick and now on importing each GPX file the correct run details, route etc all showed up – so all was good.

Here’s the PowerShell script – it logs in to your account, screen scrapes all your activities and then downloads the GPX for each. It also ‘fixes’ the downloaded GPX so it has the correct namespace :

$email = "your_email"
$password = "your_password"

$url = ""


     [System.Net.HttpWebRequest] $request = [System.Net.HttpWebRequest] [System.Net.WebRequest]::Create($url)
     $cookieJar = new-object "System.Net.CookieContainer"
     $request.CookieContainer = $cookieJar
     $request.Method = "POST"
     $request.ContentType = "application/x-www-form-urlencoded"
     $param = "Email=" + $email + "&Password=" + $password + "&login=Login"

     $request.ContentLength = $param.Length

     [System.IO.StreamWriter] $stOut = new-object System.IO.StreamWriter($request.GetRequestStream(), [System.Text.Encoding]::ASCII)

     "Logging in..."

     [System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()

     if ($response.StatusCode -ne 200)
           $result = "Error : " + $response.StatusCode + " : " + $response.StatusDescription
           $sr = New-Object System.IO.StreamReader($response.GetResponseStream())
           $txt = $sr.ReadToEnd()
           $cutstart = $txt.Substring($txt.IndexOf('<table id="my_workouts"'))
           $cutend = $cutstart.Substring(0,$cutstart.IndexOf("</div>"))

           "Getting workouts"
           $workouts = @()
           $ipos = 0
           while(($ipos -ne -1) -and ($ipos -lt ($cutend.Length -1)))
                $s = $cutend.IndexOf("<tr id=", $ipos)
                if ($s -ne -1)
                    $e = $cutend.IndexOf("</tr>", $s)
                    $e = -1
                if(($e -ne -1) -and ($s -ne -1))
                    $tr = $cutend.Substring($s, ($e + 5) - $s)
                    $workouts += $tr
                $ipos = $e

           #$workouts | %{ $id = $_.Substring(11,6); $id }

           "Got " + $workouts.Length + " workouts"
           foreach($wo in $workouts)
                $id = $wo.Substring(11,6)
                $s = $wo.IndexOf("dateval_$id") + 23
                $dt = (New-Object "System.dateTime"(1970,1,1)).AddMilliseconds($wo.Substring($s,13))
                $s = $wo.IndexOf("td_number clickDetails") + 24
                $e = $wo.IndexOf("</td>", $s)
                $dist = $wo.Substring($s, $e-$s).Trim()
                $dist = $dist.Substring(0, $dist.Length-2)
                $s = $wo.IndexOf("td_number clickDetails", $e) + 24
                $e = $wo.IndexOf("</td>", $s)
                $time = $wo.Substring($s, $e-$s).Trim()
                $s = $wo.IndexOf("td_number clickDetails", $e) + 24
                $e = $wo.IndexOf("</td>", $s)
                $cals = $wo.Substring($s, $e-$s).Trim()
                $cals = $cals.Substring(0, $cals.Length-4)
                "Workout on $dt ( ID = $id ) : $dist : $time : $cals calories"

                # now grab the GPX
                $filename = "c:scriptssportypal" + $dt.ToString("yyyy-MM-dd_HHmm") + ".gpx"
                $gpxUrl = "$id"
                [System.Net.HttpWebRequest] $gpxRequest = [System.Net.HttpWebRequest] [System.Net.WebRequest]::Create($gpxUrl)
                $gpxRequest.CookieContainer = $request.CookieContainer
                $gpxRequest.AllowWriteStreamBuffering = $false
                $gpxResponse = [System.Net.HttpWebResponse]$gpxRequest.GetResponse()
                [System.IO.Stream]$st = $gpxResponse.GetResponseStream()

                # write to disk
                $mode = [System.IO.FileMode]::Create
                $fs = New-Object System.IO.FileStream $filename, $mode
                $read = New-Object byte[] 256
                [int] $count = $st.Read($read, 0, $read.Length)
                while ($count -gt 0)
                    $fs.Write($read, 0, $count)
                    $count = $st.Read($read, 0, $read.Length)
                "- GPX Data in $filename"



    # now fix the namespace error
    $files = gci "c:scriptssportypal*.*"
    foreach ($file in $files)
        "Fixing namespace error in " + $file
        $content = get-content $file
        $content[0] = $content[0].Replace('xmlns=""', 'xmlns=""')
        Set-Content $file $content


Feel free to use this (at your own risk – I accept no liability whatsoever), but realise that you may be breaking all manner of T’s & C’s for SportyPal.

… and on a final note to anyone from SportyPal :

Sorry. I persevered, I really did, I was ready to throw money at you for a PRO subscription, I could even have lived with a further slipped release date, but not keeping your fans updated, giving no idea of a roadmap – doesn’t make us feel the love”.

GEO: 51.4043807983398 : -1.2872029542923

UPDATE: Thanks to Ricardo, who identified a bug where workouts marked as private would not download correctly. The script is now updated with this fix.

Twitter oAuth from C#

A while back I was working on a ‘post to Twitter’ function that used the original Basic Authentication that the Twitter V1.0 API allowed.

Unfortunately, at the end of Aug 2010 they discontinued support for this and forced everyone to use their oAuth authentication. There are a number of services around (such as SuperTweet) that will proxy your Basic Auth to Twitters oAuth, but really your best bet is to upgrade your code to support oAuth.

It is actually not as difficult as it may sound – here’s how I upgraded my code :-

First go to and register your application. When complete you’ll get the details you need in a page like this :


You do need to make sure that you have enabled both Read and Write access if you plan to post updates from your application.

Next, grab this zip file with the oAuth.cs and oAuthTwitter.cs classes that Eran Sandler wrote and Shannon Whitley extended, and add those to your project (make sure you change the namespace !)

Now in your class you need to create an oAuth object and assign the ConsumerKey and ConsumerSecret properties to the values given to you in the Twitter API settings page. Now when someone wants to Authorize your app to work with their account you need to get the authorization link and fire up a browser navigating to that link :

oAuthTwitter _oAuth = new oAuthTwitter();

_oAuth.ConsumerKey = "AAAAAAAAVGJTAZerhSFsafvg";
_oAuth.ConsumerSecret = "AAAAAAAgSgLfwFZDSQ3AZNDA5LwMfPnmPJud7kbCzo";

string authLink = _oAuth.AuthorizationLinkGet();

The result of them clicking ‘Allow’ in the web browser is a PIN. You need to use that PIN in a call to AccessTokenGet which finally populates the Token and TokenSecret properties of the oAuth object.

_oAuth.AccessTokenGet(_oAuth.OAuthToken, thePin);
string Token = _oAuth.Token;
string TokenSecret = _oAuth.TokenSecret;

Now (very important) you need to save the Token and TokenSecret for later use (you don’t want your user having to authorize for every update).
To send an update is now a simple affair – create an oAuthTwitter object, set the properties and then use the oAuthWebRequest function:

oAuthTwitter _oAuth = new oAuthTwitter();

_oAuth.ConsumerKey = "12345GJITAZerhSFsafvg";
_oAuth.ConsumerSecret = "lkjghKJGHblgLfwFZDSQ3AZNDA5RLwMfPnmPJud7kbCzo";
_oAuth.PIN = thePin;
_oAuth.Token = theToken;
_oAuth.TokenSecret = theTokenSecret;

string url = "";
string tweet = System.Web.HttpUtility.UrlEncode("status=" + "Tweet from my application!");
string xml = _oAuth.oAuthWebRequest(oAuthTwitter.Method.POST, url, tweet);

We’re done ! Less than 20 lines of code.

GEO: 51.4043862206013 : -1.28720283508302

PowerMeter from PowerShell

image I was trying to get my Home Energy Monitor application working to Google PowerMeterimage this evening. To get things moving quickly I decided to prototype in PowerShell (as you the full sugary goodness of the .Net framework for free). Here’s the details on accessing PowerMeter from PowerShell…

Although it is called Google PowerMeter, it is simply a service that records variables (of either cumulative or instantaneous values).

Firstly you need to get registered for an account, and it is not obvious how you actually get a Google PowerMeter account if you don’t have some of the supported devices or don’t have a contract with one partner utility companies. The easy way is to put together a url that basically requests a new account. the format of the url is :-

All the details are on this page. I have a CurrentCost Envi, so my url became:

Note I’m using dvars at the end instead of cvar – dvars are for durational measurements and cvars are for instantaneous measurements – you need to get these right or your uploads will fail. the dvars=1 means I want only 1 variable (energy), I could have opted for more (dvars=2, or dvars=3 etc), but 1 will do for now.

imageWhen you’ve created your url, simply browse to it. Google will authenticate you with your usual Google account and then ask you to give a friendly name to the variable(s) you created. When complete you’ll be presented with a activation code. You can get this activation again by browsing to your settings page in Google PowerMeter.  From this activation code you need 3 pieces of data as highlighted below :


The first is your ‘auth token’, the second is your ‘user id’ and the third is your ‘device id’.

Now for the PowerShell script. It is fairly simple, it creates an Xml string with the start date of the reading, the duration (in seconds) and the value and then uploads this to Google PowerMeter. It does need some Headers adding first to make sure your sending the correct Content-Type and to make sure you are authorized…

$user = "YOUR USER ID"
$device = "YOUR DEVICE ID"
$variable = "d1"
$url = ""
$var = "$user/$user/variable/$device.$variable"

$start = [System.Xml.XmlConvert]::ToString([System.DateTime]::Now)
$duration = 1
$energy = 9999

$xmlData = @"
    <feed xmlns=`"`"
        <category scheme=`"`"
        <meter:startTime uncertainty=`"1.0`">{1}</meter:startTime>
        <meter:duration uncertainty=`"1.0`">{2}</meter:duration>
        <meter:quantity uncertainty=`"0.001`" unit=`"kW h`">{3}</meter:quantity>

$rdgData = [string]::Format($xmlData, $var, $start, $duration, $energy)

$wc = New-Object System.Net.WebClient
$whc = New-Object System.Net.WebHeaderCollection$res
$whc.Add("Content-Type", "application/atom+xml")
$whc.Add("Authorization", "AuthSub token=`"$auth`"")
$wc.Headers = $whc

$response = $wc.UploadString($url, "POST", $rdgData)

Now you have the xml response in the $response variable. To check this you can simply ([xml]$response).feed.entry.status.code – you’re looking for a 201 (‘Created’).
You should now have a measurement lodged with Google PowerMeter !!  Enjoy…

GEO 51.4043388366699:-1.2875679731369

CurrentCost Power Monitor Software – Take 2


This post is an update to my previous one about the CurrentCost Energy Monitor and the software I built for it.

After posting brief details about it, I agreed with the guys over at the MSDN Coding for Fun site to write a full project article  for them. The suggested a couple of changes – using the Managed Extension Framework (MEF) stuff available in .NET 4 and a Windows Phone 7 client, to make it more generally appealing.

I had not seen the MEF stuff before, but it turned out great – very simple framework that allows a full ‘plugin’ style architecture in around 10 lines of code. I also had little experience with the Xaml stuff required for WinPhone 7 apps, but it is actually pretty simple.

There are a number of plugins and clients for it now including :

            • A plugin to post to Twitter every X minutes (using oAuth)
            • A plugin to post to Pachube streams
            • A plugin to post to a web service
            • A web page to display the latest readings sent to the web service
            • A Windows Phone 7 application to display the latest reading sent to the web service
            • A Windows Sidebar Gadget to display the latest reading sent to the web service

I have also had submissions from a couple of other developers, one for a plugin that posts to Google PowerMeter and another that records the readings in a database – both of these will be uploaded to the Codeplex site when I have them integrated into the source and working correctly.

Anyway, you can read all about it here. There is also a Codeplex site for all the source code and binaries.

Post in the comments if you have suggestions for other plugins or features, or want to get involved.

GEO 51.4043388366699:-1.2875679731369

CurrentCost Power Monitor Software

cc128-large_01 I recently bought a device to monitor our household energy consumption. After looking at a few I lumped for a CurrentCost Envi. this is a great little device that comes in two parts – a transmitter with jaws that wrap around the main power cable coming into your home and a desktop display unit. The communication is all wireless and I have found that it works okay through two thick brick walls.

the reason for going with this particular unit was that it has a data port that can connect to a USB port and feed it’s readings to a PC – I wanted to be able to monitor the readings on my PC and chart/analyse them at will.

imageThe communication to the PC is basically via USB, but emulating a serial port COM connection – the CurrentCost website has links to the drivers you’ll need for this.

I wanted to, not only, record the readings but to chart them (on my website), get regular updates via various notification methods and a few other bits. Unfortunately, none of the software applications listed on their website covered all the items that I wanted – so I set about writing something myself…

My initial thoughts were around just sending the data to Google PowerMeter, but at that stage the API was not public and their forums/groups were not very active (6 posts in about 18 months), so I abandoned that and decided a ‘DIY’ approach was needed.
UPDATE: It looks like there may be some substance to it now, so that is another area to look at (another plugin)…

imageThe requirements were pretty simple – I wanted a service that grabbed the readings as they came in, decoded them into something intelligible and then pushed them out to a number of ‘modules’ that would do something with the reading. the ‘modules’ would be self contained and new modules could be added at any time. The initial modules would be :-

  • Tweet the reading at a regular interval
  • Record the reading (in a database, Xml file or the like)
  • Send the reading to a website / webservice

It seemed a simple Windows Service with a COM port would be enough to grab the reading, the readings are all in Xml so another class to parse the Xml would be needed and then for the ‘modules’ a ‘plugin’ type architecture was called for. I came up with an interface that all plugins would implement and a method of loading them in dynamically.

Each plugin would inherit from the IPowerMonitorPlugin interface and to load the plugins, each one would be specified in the app.config file with a filename and classname. The service would look at each plugin entry, load the DLL and create an instance of the plugin class :-

    private void LoadPlugins()
        plugins = new ArrayList();

        NameValueCollection appSettings = ConfigurationManager.AppSettings;
        foreach (string key in appSettings.AllKeys)
                string path = AppDomain.CurrentDomain.BaseDirectory;
                string[] config = appSettings.Get(key).Split(':');
                Assembly ass = Assembly.LoadFile(path + config[0]);
                IPowerMonitorPlugin plugin = (IPowerMonitorPlugin)ass.CreateInstance(config[1]);
                if (null == plugins)
                    plugins = new ArrayList();
                if (!plugins.Contains(plugin))


When the service was working and decoding the readings correctly, I started adding plugins – first was a simple ‘RawXmlWriterPlugin’ – this simple wrote the raw Xml reading data (Reading.RawXml) out to a text file – just to make sure it was working and we were decoding the Xml correctly.

imageThe next service was posting the data to a website – I found this great website ( which allows you to track environmental data measurements, have multiple feeds, multiple measurements and has a lot of options for getting data in and out. The API that they provide is pretty simple to push data into and their website allows plenty of ways to visualize the data – for example here is a link to my last 24 hours energy consumption in a chart format, here is my latest temperature reading and here is an archive of all the energy data I have ever posted. there are also mash up to things like iGoogle widgets, Google’s Visualization API, Google Sketchup, iPhone Apps, Android Apps and this rather neat gauge.

From there I started the Twitter service….



The plugin for posting to Pachube requires that you have already create a feed with two datastreams (datastream 0 is energy and datastream 1 is temperature). You will need an API Key and the feed id. Both these items are configured in the app.config file also.


The plugin for ‘tweeting’ to twitter is also pretty simple – all you need is an account (username and password) and the ‘handle’ of the person/account you want to send the message to (if you want to send a direct message). You also specify the message text you wish to send (with placeholders for the energy and temperature values) an the interval (in minutes) of how frequently you wish to send the tweets.

SQL / Xml Storage Plugin

Watch this space … 😉

Source and Package

For the time being either send me an email or post in the comments if you’d like access to the source code.

If you want to run this software yourself then here is a link to a zip file containing the full package. To get it installed, do the following :

  • imageUnzip the package contents into a folder (“C:Program FilesPowerMonitorService” would be good)
  • Open a command prompt and change to the folder above
  • Run the following command line…

“C:WindowsMicrosoft.NETFramework64v2.0.50727InstallUtil.exe” PowerMonitorService.exe (if you are running a 64 bit machine… or)
“C:WindowsMicrosoft.NETFrameworkv2.0.50727InstallUtil.exe” PowerMonitorService.exe (if you are running a 32 bit machine… or)


  • Open the PowerMonitorService.exe.config file (in Notepad) and edit your configuration as needed – save when done.
  • Now start the service (, find the one named PowerMonitorService, right click and choose “Start”)


GEO 51.4043388366699:-1.2875679731369

Install Windows from a USB Drive

USBdriveThis post is for my own benefit more than anything. I frequently have to ‘google’ for the instructions to make a bootable USB drive to install some version of Windows from. So, to save time in the future, here are the instructions :

Open a command prompt As Administrator. Run diskpart. Enter list disk – this will list all the attached disks, make note of the USB drive disk number (2 in my case, so substitute 2 in the commands below for whatever number it shows on your system :- image

  • select disk 2
  • clean
  • create partition primary
  • select partition 1
  • active
  • format fs=fat32 quick
  • assign
  • exit




GEO 51.4043388366699:-1.2875679731369

Reading List – March 2010

After a brief hiatus (i.e. hectic work schedule for a few months), I am back to reading. The list is currently :-

Actually, that is wrong. I picked up ‘ReWork’ by the 37 Signals guys and read it cover to cover, so that’s no longer on the list 😉   – what a great book. Full of down to earth pragmatic advice and common sense (as you’d expect from these guys), a ton of snippets to take away and really inspiring. I must read this again to make sure I got everything.

GEO 51.4043388366699:-1.2875679731369