<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.dantup.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
	<id>tag:blog.dantup.com,2009:/</id>
	<updated>2018-02-01T21:43:09+00:00</updated>
	<title type="text">Danny Tuppeny</title>
	
	<link rel="alternate" type="text/html" href="https://blog.dantup.com/" />
	<author>
		<name>Danny Tuppeny</name>
		<uri>https://blog.dantup.com/</uri>
	</author>
	
	<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.dantup.com/DanTup" /><feedburner:info uri="dantup" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
		<id>tag:blog.dantup.com,2017-07-11:/2017/07/dart-code-v2-now-with-flutter-debugging-and-hot-reload/</id>
		<published>2017-07-11T00:00:00+00:00</published>
		<updated>2017-07-11T00:00:00+00:00</updated>
		
			<category term="Dart" />
		
			<category term="Open Source" />
		
			<category term="Visual Studio Code" />
		
		<title type="text">Dart Code v2 - Now with Flutter debugging and hot reload!</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/Dart-Code/Dart-Code"&gt;&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s already been ten months since I published the first preview of &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;Dart Code&lt;/a&gt; and today marks a significant milestone - version 2.0! It’s been some time in the making; this major update adds support for launching and debugging Android and iOS apps written using &lt;a href="https://flutter.io/"&gt;Flutter&lt;/a&gt;, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When a Flutter project is open your selected device/emulator will be shown in the status bar&lt;/li&gt;
  &lt;li&gt;If you have more than one connected device/emulator, clicking the device name in the status bar will allow you to switch between devices/emulators&lt;/li&gt;
  &lt;li&gt;Pressing &lt;code class="highlighter-rouge"&gt;F5&lt;/code&gt; with a flutter project open will build and launch your app on the selected device/emulator&lt;/li&gt;
  &lt;li&gt;The usual debugging experience is available for Flutter apps, including breakpoints, call stacks, watch window etc.&lt;/li&gt;
  &lt;li&gt;The debugger’s &lt;code class="highlighter-rouge"&gt;Restart&lt;/code&gt; button (or &lt;code class="highlighter-rouge"&gt;Ctrl+Shift+F5&lt;/code&gt;) has been mapped to Flutter’s &lt;code class="highlighter-rouge"&gt;hot reload&lt;/code&gt; feature and will update the running application without needing to rebuild (this usually results in sub-second updates!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://twitter.com/BramVanbilsen"&gt;Bram Vanbilsen&lt;/a&gt;, who has started &lt;a href="https://www.youtube.com/playlist?list=PLxU9Ryxq6p58PsNmJL70J4_7UzfSqf35n"&gt;an excellent set of Flutter tutorials on YouTube&lt;/a&gt; has created a short video showing how this integration works with a demo of hot reload:&lt;/p&gt;

&lt;div align="center"&gt;
	&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/hhP1tE-IHos?VQ=HD1080" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id="download"&gt;Download&lt;/h2&gt;

&lt;p&gt;In order to get up and running you’ll need to grab a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; version 1.13.0 or later&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;v2 of Dart Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A recent version of the &lt;a href="https://www.dartlang.org/install"&gt;Dart SDK&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;For Flutter support you’ll also need to &lt;a href="https://flutter.io/setup/"&gt;set up Flutter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, to avoid manual configuration you should also add the Dart and Flutter SDKs to your &lt;code class="highlighter-rouge"&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="feedback"&gt;Feedback&lt;/h2&gt;

&lt;p&gt;If you do try it out, I’d love your feedback. You can find me on Twitter (&lt;a href="https://twitter.com/DanTup"&gt;@DanTup&lt;/a&gt; and &lt;a href="https://twitter.com/DartCode"&gt;@DartCode&lt;/a&gt;), post issues at &lt;a href="https://github.com/Dart-Code/Dart-Code/issues"&gt;GitHub/Dart-Code&lt;/a&gt; or find me in the &lt;a href="https://gitter.im/dart-code/Dart-Code"&gt;Gitter chat room&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id="thanks"&gt;Thanks&lt;/h2&gt;

&lt;p&gt;I’ve never blogged about Dart Code before so I’ve never really said thanks to all those that helped make it possible. It’s a long list! Dart Code builds on a lot of excellent work done by many people at Google (including the analyzer, which powers most of the language and editor support, the VM and debugger, the flutter tooling) and of course many people at Microsoft working on VS Code. I’ve opened a lot of issues, sent a lot of emails and asked a lot of questions in chat rooms for both projects and I’ve had great support. I’ve also had some &lt;a href="https://github.com/Dart-Code/Dart-Code/graphs/contributors"&gt;great contributions&lt;/a&gt; including the bulk of the debugger work from &lt;a href="https://github.com/devoncarew"&gt;Devon&lt;/a&gt; and many &lt;a href="https://github.com/Dart-Code/Dart-Code/issues/318"&gt;beta testers&lt;/a&gt; who helped iron out issues in the Flutter integration before I unleashed it!&lt;/p&gt;

&lt;h2 id="up-next---web-support"&gt;Up Next - Web Support&lt;/h2&gt;

&lt;p&gt;With Flutter integration released the next major feature for Dart Code is likely to be better support for web apps, including &lt;code class="highlighter-rouge"&gt;F5&lt;/code&gt; to launch and incremental compilation. Stay tuned to &lt;a href="https://twitter.com/DartCode"&gt;@DartCode on Twitter&lt;/a&gt; for update notifications!&lt;/p&gt;


			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2017/07/dart-code-v2-now-with-flutter-debugging-and-hot-reload/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/l8syIhGkp-g" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/l8syIhGkp-g/" title="Dart Code v2 - Now with Flutter debugging and hot reload!" />
	<feedburner:origLink>https://blog.dantup.com/2017/07/dart-code-v2-now-with-flutter-debugging-and-hot-reload/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2017-02-03:/2017/02/i-found-a-ridiculous-bug-in-youtube-and-seem-unable-to-successfully-report-it/</id>
		<published>2017-02-03T00:00:00+00:00</published>
		<updated>2017-02-03T00:00:00+00:00</updated>
		
			<category term="YouTube" />
		
		<title type="text">I found a ridiculous bug in YouTube and seem unable to successfully report it :(</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Last week a colleague tweeted me telling me that the hyperlinks in the descriptions of &lt;a href="https://www.youtube.com/watch?v=GKI7aKY_hxY"&gt;my kids review of the Echo Dot&lt;/a&gt; were giving 404s. This surprised me because I’d clicked them to ensure they worked after publishing the video! I probed him and he sent a screenshot of 404s. I checked them again myself both on desktop and my mobile and everything was fine. Since this colleague has a certain sense of humour, I figured he was trying to wind me up and didn’t think much of it.&lt;/p&gt;

&lt;blockquote class="bigquote right"&gt;
YouTube is truncating links not just for the display but the actual URL the user is taken to
&lt;/blockquote&gt;

&lt;p&gt;The next day he showed me the issue on his mobile phone - he was right - the links had been &lt;strong&gt;truncated&lt;/strong&gt;! WTF? I noticed that when he clicked YouTube links in twitter they were opening in Chrome, not the YouTube app. I tried the same thing on my mobile and was immediately able to reproduce the issue. I did some Googling and found reports of &lt;a href="https://productforums.google.com/forum/#!topic/youtube/uDB4NY6kEvc"&gt;a bug in 2013&lt;/a&gt; that sounded similar. I dug into this and discovered that this wasn’t the same issue - that was an issue with the inline editor truncating links when editing; my links were fine in the editor and on desktop/mobile. This is a different bug. The YouTube mobile site is truncating links not just for the display but the &lt;em&gt;actual URL the user is taken to&lt;/em&gt;. Whaaaaa?&lt;/p&gt;

&lt;p&gt;You might think “Who uses the mobile site anyway? Just Windows Phone users?” but the colleague that reported it to me has an Android phone that came pre-installed with the YouTube app, yet clicking YT links in Twitter opens the mobile web version. Lots of promotion of YouTube videos is done on Twitter and other social media so the number of people hitting this bug is probably not insignificant. Probably they assume the video creator is a muppet rather than a YouTube bug.&lt;/p&gt;

&lt;p&gt;I did some more Googling and could not find a single report of this bug online! I’m surprised that a bug with links being truncated for a large number of people (if only 1% of YouTube traffic is mobile web, that’s still huge) can go unnoticed.&lt;/p&gt;

&lt;p&gt;I decided to check it wasn’t just me so I opened up some videos from popular YouTubers I follow… Casey Neistat…  Same thing.&lt;/p&gt;

&lt;p class="bdr"&gt;&lt;img src="/post_images/yt_bug_casey.jpg" alt="404 from Casey Neistat's YouTube video" /&gt;&lt;/p&gt;

&lt;p&gt;I tried Sean Cannell… Same thing.&lt;/p&gt;

&lt;p class="bdr"&gt;&lt;img src="/post_images/yt_bug_sean.jpg" alt="404 from Sean Cannell's YouTube video" /&gt;&lt;/p&gt;

&lt;p&gt;I tried MKBHD… Same thing.&lt;/p&gt;

&lt;p class="bdr"&gt;&lt;img src="/post_images/yt_bug_mkbhd.jpg" alt="404 from MKBHD's YouTube video" /&gt;&lt;/p&gt;

&lt;p&gt;Sara Dietschy… Same thing.&lt;/p&gt;

&lt;p class="bdr"&gt;&lt;img src="/post_images/yt_bug_sara.jpg" alt="404 from Sara's YouTube video" /&gt;&lt;/p&gt;

&lt;p&gt;What the fuck YouTube? Where is the testing? &lt;a href="https://blog.dantup.com/2016/04/have-software-developers-given-up/"&gt;Have we given up?&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, I did what I always do when I find a bug. I tried to report it. Quite a few times…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://productforums.google.com/forum/#!msg/youtube/BlU8eV2AQQo/zYR16f6XDAAJ"&gt;In the official forums&lt;/a&gt; - no responses from employees nor top contributors&lt;/li&gt;
  &lt;li&gt;Via Send Feedback on the web - I use this a lot in Google apps and they all feel like black holes&lt;/li&gt;
  &lt;li&gt;Via Send Feedback in the mobile app - as above&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://twitter.com/DanTup/status/826488712557101058"&gt;Twitter&lt;/a&gt; (&lt;a href="https://twitter.com/DanTup/status/826500598249857024"&gt;three&lt;/a&gt; &lt;a href="https://twitter.com/DanTup/status/826907619323412481"&gt;times&lt;/a&gt;) - no response here (despite the YouTube account responding to others every day)&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.reddit.com/r/youtube/comments/5ra0gg/bug_mobile_web_version_of_youtube_truncates_links/"&gt;In /r/YouTube/&lt;/a&gt; knowing employees post there; it fell off the homepage with no responses (and even downvotes)&lt;/li&gt;
  &lt;li&gt;I tweeted &lt;a href="https://twitter.com/DanTup/status/826490526157434881"&gt;some&lt;/a&gt; &lt;a href="https://twitter.com/DanTup/status/827438719036694528"&gt;of&lt;/a&gt; &lt;a href="https://twitter.com/DanTup/status/827437746100711424"&gt;the&lt;/a&gt; &lt;a href="https://twitter.com/DanTup/status/827438073814335488"&gt;YouTubers&lt;/a&gt; I followed to alert them of this issue (it’s lost revenue for those putting affiliate links here and maybe they have contacts?) though the only response I got &lt;a href="https://twitter.com/seancannell/status/826495961937555456"&gt;wasn’t clear if the YouTuber really understood&lt;/a&gt; what I was saying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only workaround for this bug that I know of is to shorten all your links. I hate this because it looks scummy like you’re trying to hide something. I created my own short links for the &lt;a href="https://www.youtube.com/channel/UCJxVQCWS4O_c38fPp5stDxw"&gt;Review Kidz videos&lt;/a&gt; for now that hopefully makes it clearer where they go than goo.gl but it’s not a great solution.&lt;/p&gt;

&lt;p&gt;So, this blog post is my final attempt to get someone from YouTube/Google to see this so they might consider fixing this (IMO completely ridiculous) bug. I’d rather not have to shorten links because of this but it’s looking like that’s how it’s going to be. It’s amazing both that a product as large as YouTube can have so many broken links and apparently nobody has noticed and that there’s very little information on how to report bugs or get their attention (though honestly, this seems par for the course with Google these days; support is simply nonexistent).&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2017/02/i-found-a-ridiculous-bug-in-youtube-and-seem-unable-to-successfully-report-it/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/HdFB7CboMqk" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/HdFB7CboMqk/" title="I found a ridiculous bug in YouTube and seem unable to successfully report it :(" />
	<feedburner:origLink>https://blog.dantup.com/2017/02/i-found-a-ridiculous-bug-in-youtube-and-seem-unable-to-successfully-report-it/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2017-01-27:/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/</id>
		<published>2017-01-27T00:00:00+00:00</published>
		<updated>2017-01-27T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Dart" />
		
			<category term="Raspberry Pi" />
		
		<title type="text">Simple Dart code to tweet using OAuth</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;blockquote&gt;
  &lt;p&gt;Writing Dart? Check out &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;Dart Code, my Dart extension for Visual Studio Code!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last year &lt;a href="/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/"&gt;I blogged about the simplest C# code I could come up with to post tweets&lt;/a&gt; without having to jump through OpenAuth hoops. This only works for accounts you can get keys for (eg. your own) but it’s significnatly simpler than the normal OAuth flow.&lt;/p&gt;

&lt;p&gt;Since running C# on my &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt; turned out to be &lt;a href="https://github.com/dotnet/cli/issues/2556"&gt;significantly more effort&lt;/a&gt; than I expected from the “cross platform” .NET Core (I did try mono but getting SSL working to talk to Twitter was a clusterfuck of pasting commands from the internet that didn’t work) I decided to port it to one of my favourite languages, Dart! It would also be a nice task to test out &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;Dart Code&lt;/a&gt;, the Dart plugin I created for Visual Studio Code (with much support from the Dart and VS Code teams) which I don’t get to do nearly enough of.&lt;/p&gt;

&lt;p&gt;Like with &lt;a href="/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/"&gt;the C# version&lt;/a&gt; it turned out to be complicated to encode strings in the way that Twitter expects. I tried all sorts of encoding methods built-in to Dart and none of them gve the expected results. The &lt;a href="https://pub.dartlang.org/packages/convert"&gt;convert package&lt;/a&gt; published by the Dart team claimed to be RFC 3986 compliant but it seemed to encode numbers - &lt;code class="highlighter-rouge"&gt;1&lt;/code&gt; became &lt;code class="highlighter-rouge"&gt;%31&lt;/code&gt; and Twitter didn’t like that. Assuming this was a bug, I &lt;a href="https://github.com/dart-lang/convert/issues/3"&gt;raised an issue&lt;/a&gt; and &lt;a href="https://github.com/dart-lang/convert/pull/4"&gt;sent a PR with tests and a fix&lt;/a&gt;. It took a little while but my PR was merged and a new version of the package released with the fix.&lt;/p&gt;

&lt;p&gt;If you want the details about how to get your API keys to use this code, read the &lt;a href="/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/"&gt;the C# version of this post&lt;/a&gt; which contains links to the docs at Twitter. The rest of this article is just a summary of the actual Dart code. It’s very much a direct port of the C# tidied up where Dart made it easy. If I’m honest, I think the Dart version is much less readable due to &lt;code class="highlighter-rouge"&gt;dartfmt&lt;/code&gt; wrapping awkwardly. I do format using this currently but I’m very much on the fence about whether it’s worthwhile sacrificing readability and the ability to use tabs for formatting (I think there are better ways of achieving the consistency they aim for).&lt;/p&gt;

&lt;p&gt;On to the code!&lt;/p&gt;

&lt;p&gt;As before, the constructor takes in the keys and creates a hasher. &lt;em&gt;(Note: Indenting is &lt;code class="highlighter-rouge"&gt;dartfmts&lt;/code&gt;;  two spaces for indenting but 4 spaces for wrapped lines.. I’d prefer it if the top line was unwrapped)&lt;/em&gt;.&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;TwitterApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$consumerKeySecret&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;$accessTokenSecret&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;_sigHasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Hmac&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The tweet method is async and just builds the payload before calling a re-usable &lt;code class="highlighter-rouge"&gt;_callApi&lt;/code&gt; method (note: in Dart underscore prefixes automatically make things private). This will allow adding additional methods easier (as &lt;a href="https://blog.dantup.com/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/#comment-3112597725"&gt;Eelco Koster did with the C# version&lt;/a&gt;). I set &lt;code class="highlighter-rouge"&gt;trim_user&lt;/code&gt; just to reduce the amount of data that comes back in the response (since I don’t use it for anything).&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Sends a tweet with the supplied text and returns the response from the Twitter API.&lt;/span&gt;
&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"trim_user"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_callApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"statuses/update.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class="highlighter-rouge"&gt;_callApi&lt;/code&gt; method is almost identical to the C# &lt;code class="highlighter-rouge"&gt;SendRequest&lt;/code&gt; method so I’ll skip over it here (the full code at the bottom of the post).&lt;/p&gt;

&lt;p&gt;I extracted some helpers for making query strings and OAuth headers (which annoyingly were subtly different; one requires quotes and one can’t have them) which made the signature and header generation methods pretty terse:&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Generate an OAuth signature from OAuth header values.&lt;/span&gt;
&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;_generateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sigString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_toQueryString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullSigData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"POST&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;${_encode(url.toString())}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;${_encode(sigString)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;BASE64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSigData&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// Generate the raw OAuth HTML header from the values (including signature).&lt;/span&gt;
&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;_generateOAuthHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;oauthHeaderValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_filterMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"OAuth "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;_toOAuthHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauthHeaderValues&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next is a method for sending the request over to Twitter. I originally missed the &lt;code class="highlighter-rouge"&gt;http.close&lt;/code&gt; from the end of this which resulted in a 10-15 second hang when my program finished (the Dart VM didn’t exit until the connection timed out). I don’t really like that you need to add that (it’s easily forgotten and a pain to debug/understand) but I don’t know of a good fix.&lt;/p&gt;

&lt;p&gt;This is the most unreadable part of this class IMO after running through &lt;code class="highlighter-rouge"&gt;dartfmt&lt;/code&gt; &lt;em&gt;(I would put breaks/indent before &lt;code class="highlighter-rouge"&gt;.then&lt;/code&gt;’s and unwrap the &lt;code class="highlighter-rouge"&gt;contentType&lt;/code&gt; header)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Slightly more readable after switching to &lt;code class="highlighter-rouge"&gt;await&lt;/code&gt; from &lt;code class="highlighter-rouge"&gt;.then&lt;/code&gt; (thanks &lt;a href="#comment-3123212558"&gt;Benjamin Campbell!&lt;/a&gt;).&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Send HTTP Request and return the response.&lt;/span&gt;
&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sendRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;postUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x-www-form-urlencoded"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;charset:&lt;/span&gt; &lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The class is used in the same way as the C# version and the response from &lt;code class="highlighter-rouge"&gt;tweet&lt;/code&gt; awaited:&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;twitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TwitterApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;liveConsumerKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;liveConsumerKeySecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;liveAccessToken&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;liveAccessTokenSecret&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YOUR TWEET HERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since my need is a very simple script where I receive the output my email, my error handling is very lame:&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;errors&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to send tweet:"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweetToSend&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Success!&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If using this more seriously, you’d want to return a nice class from the &lt;code class="highlighter-rouge"&gt;tweet&lt;/code&gt; method instead.&lt;/p&gt;

&lt;p&gt;Here’s the full class; you’ll need to add &lt;code class="highlighter-rouge"&gt;convert&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;crypto&lt;/code&gt; to your &lt;code class="highlighter-rouge"&gt;pubspec.yaml&lt;/code&gt; and run &lt;code class="highlighter-rouge"&gt;pub get&lt;/code&gt; to get this working.&lt;/p&gt;

&lt;div class="language-dart highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:io'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:convert/convert.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:crypto/crypto.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;twitterApiBaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.twitter.com/1.1/"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TwitterApi&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Hmac&lt;/span&gt; &lt;span class="n"&gt;_sigHasher&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;_epochUtc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1970&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// TwitterApi class adapted from DanTup:&lt;/span&gt;
  &lt;span class="c1"&gt;// https://blog.dantup.com/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/&lt;/span&gt;

  &lt;span class="n"&gt;TwitterApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$consumerKeySecret&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;$accessTokenSecret&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_sigHasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Hmac&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Sends a tweet with the supplied text and returns the response from the Twitter API.&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"trim_user"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_callApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"statuses/update.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_callApi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitterApiBaseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Timestamps are in seconds since 1/1/1970.&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toUtc&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;difference&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_epochUtc&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;inSeconds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Add all the OAuth headers we'll need to use when constructing the hash.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_consumer_key"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_signature_method"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HMAC-SHA1"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_timestamp"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_nonce"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Required, but Twitter doesn't appear to use it&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_token"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_version"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate the OAuth signature and add it to our payload.&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"oauth_signature"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_generateSignature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Build the OAuth HTTP Header from the data.&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_generateOAuthHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Build the form data (exclude OAuth stuff that's already in the header).&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_filterMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_sendRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_toQueryString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Generate an OAuth signature from OAuth header values.&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_generateSignature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sigString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_toQueryString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullSigData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"POST&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;${_encode(url.toString())}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;${_encode(sigString)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;BASE64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSigData&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Generate the raw OAuth HTML header from the values (including signature).&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_generateOAuthHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;oauthHeaderValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_filterMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"OAuth "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;_toOAuthHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauthHeaderValues&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Send HTTP Request and return the response.&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sendRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;postUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;
      &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x-www-form-urlencoded"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;charset:&lt;/span&gt; &lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_filterMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromIterable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nl"&gt;value:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_toQueryString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$k&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${_encode(data[k])}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_toOAuthHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$k&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;${_encode(data[k])}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sigHasher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codeUnits&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codeUnits&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’re free to do as you please with this code. If you improve anything significantly, do leave a comment!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/dCkjpYOIUoI" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/dCkjpYOIUoI/" title="Simple Dart code to tweet using OAuth" />
	<feedburner:origLink>https://blog.dantup.com/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2017-01-14:/2017/01/easily-sending-uk-visitors-to-amazon-uk-and-others-to-amazon-us/</id>
		<published>2017-01-14T00:00:00+00:00</published>
		<updated>2017-01-14T00:00:00+00:00</updated>
		
			<category term="Affiliates" />
		
		<title type="text">Making more money with Amazon: Splitting visitors between Amazon US/UK based on their location</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Recently I was chatting with a friend in the &lt;a href="https://affiliate-program.amazon.co.uk/"&gt;UK Amazon Associates programme&lt;/a&gt;. He hadn’t realised that all of the US traffic he sends to affiliate links at amazon.co.uk didn’t count for anything as the affiliate programmes are completely separate. Most products can’t be shipped (or are expensive to) between Amazon regions so he’s missing out on a lot of earnings by not sending US visitors to amazon.com. I provided him with a little JavaScript and some instructions to rememdy this but, since he’s not a developer and is using a CMS it wasn’t exactly a simple solution having to hand-edit code to include both UK and US product identifiers (which are rarely the same) in his links for my script to work.&lt;/p&gt;

&lt;p&gt;This gave me an idea for a new service; enter &lt;a href="https://www.us-uk-redirector.com/"&gt;us-uk-redirector.com&lt;/a&gt; (aka &lt;a href="https://amzredir.com/"&gt;amzredir.com&lt;/a&gt;)!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.us-uk-redirector.com/"&gt;&lt;img src="/post_images/us_uk_redir_1.jpg" alt="US/UK Redirector" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can paste in URLs for products on amazon.com and amazon.co.uk and you’ll be given back a URL that will redirect UK visitors to amazon.co.uk and others to amazon.com. There’s nothing to install on your site and there’s no messing in markup - just paste a hyperlink in the way that you normally would. There are also separate fields for your affiliate IDs which means you can paste in any old Amazon link without having to ensure your affiliate tag is in there. They’re also persisted to local storage so next time you come back they’re pre-filled!&lt;/p&gt;

&lt;p&gt;In addition to pasting URLs you can enter ASIN numbers or search terms directly (did I mention you can paste in search results URLs too? it’ll parse them!). A banner is shown beneath each box explaining exactly what will happen to visitors for this region.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/us_uk_redir_2.jpg" alt="Entering  search terms and ASINs directly" /&gt;&lt;/p&gt;

&lt;p&gt;The URLs will automatically combine similar info to keep them short, so if the ASIN or search terms are the same, or if your affiliate tags are the same for UK and US (except for the -20 and -21 suffix) they will be combined and handled during the redirect.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/us_uk_redir_3.jpg" alt="Shorter links with combined affiliate tags" /&gt;&lt;/p&gt;

&lt;p&gt;It will even shout at you if you enter invalid URLs or affiliate tags to ensure you’re not messing up.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/us_uk_redir_4.jpg" alt="Error messages" /&gt;&lt;/p&gt;

&lt;p&gt;The URLs are simple to build yourself too, &lt;a href="https://www.us-uk-redirector.com/"&gt;the format is described on the site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re an Amazon affiliate check it out, I’d love feedback. It’ll work for non-affiliates too if you’d still like to send visitors to the correct site (or if you’re only an affiliate in one region, it’ll still work).&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2017/01/easily-sending-uk-visitors-to-amazon-uk-and-others-to-amazon-us/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/BAIo5jOntwQ" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/BAIo5jOntwQ/" title="Making more money with Amazon: Splitting visitors between Amazon US/UK based on their location" />
	<feedburner:origLink>https://blog.dantup.com/2017/01/easily-sending-uk-visitors-to-amazon-uk-and-others-to-amazon-us/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2017-01-06:/2017/01/visiting-a-site-that-uses-disqus-comments-when-not-logged-in-sends-the-url-to-facebook/</id>
		<published>2017-01-06T00:00:00+00:00</published>
		<updated>2017-01-06T00:00:00+00:00</updated>
		
			<category term="Blogging" />
		
			<category term="Privacy" />
		
		<title type="text">Visiting a site that uses Disqus comments when not logged in sends the URL to Facebook</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;em&gt;(&lt;strong&gt;Update&lt;/strong&gt;: As a result of this post Disqus fixed this issue on 9th Jan! &lt;a href="/2017/01/visiting-a-site-that-uses-disqus-comments-when-not-logged-in-sends-the-url-to-facebook/#comment-3091263180"&gt;See here&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I was making some tweaks to my blog and reviewing the page load time. I noticed that when I loaded an article the Facebook SDK was being loaded!&lt;/p&gt;

&lt;p&gt;&lt;a href="/post_images/facebook_sdk_blog.png"&gt;&lt;img src="/post_images/facebook_sdk_blog.png" alt="Facebook SDK being loaded into my blog" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was a bit strange as I didn’t recall ever having added any Facebook widgets to my blog. Not only is this somewhat wasteful and harming my load time, it also bugged me that visitors were having these URLs sent to Facebook. I’ve always thought it strange that Facebook seems to know &lt;em&gt;everywhere&lt;/em&gt; I’ve been and it seems my blog is adding to this without me even knowing!&lt;/p&gt;

&lt;p&gt;So, I checked why this file was being loaded and was surprised to see Disqus as the initiator. I do have Disqus comments on my blog but that doesn’t seem like a good reason to have the Facebook SDK loaded immediately! I &lt;a href="https://twitter.com/DanTup/status/814902576449814528"&gt;tweeted Disqus&lt;/a&gt; though unsurprisingly haven’t recieved a response.&lt;/p&gt;

&lt;p&gt;Fast forward a week and I was reading Troy Hunt’s &lt;a href="https://www.troyhunt.com/i-just-permanently-removed-all-ad-network-code-from-my-blog/"&gt;I just permanently removed all ad network code from my blog&lt;/a&gt;. Troy cited tracking as one of the reasons for removing ads (I strongly support this decision; running unknown JavaScript from ad networks on an HTTPS site always seemed a bit weird to me). In a response on twitter I noticed someone asked him about still having Disqus, which reminded me about what I’d seen on my blog and wondered if the same thing happened there that I saw on my blog.&lt;/p&gt;

&lt;p&gt;Well, it turns out it does. Here’s Troy’s blog loading the Facebook SDK and clearly sending the URL for the post I’m reading to Facebook in the referer header (it’s URL-encoded in the querystring of the Disqus comments frame URL).&lt;/p&gt;

&lt;p&gt;&lt;a href="/post_images/troy_fb_tracking.png"&gt;&lt;img src="/post_images/troy_fb_tracking.png" alt="Facebook SDK being loaded into my blog" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you might say that this isn’t really the URL of the page, it’s going via Disqus and Facebook won’t read it. I’d counter that since Facebook are very interested in tracking where you visit and Disqus is so common that it is in their best interest to understand this URL and extract the real article URL from the querystring.&lt;/p&gt;

&lt;p&gt;I did a little more digging and discovered that this only happens if you’re not logged in to Disqus. It also happens even if &lt;code class="highlighter-rouge"&gt;Do-Not-Track&lt;/code&gt; is enabled in your browser. It’s almost certainly as a result of the “Sign in with Facebook” functionality however I don’t believe it has to, not should, be this way. If I implemented “Sign in with Facebook” on my site directly then I’ve chosen to pull the Facebook SDK in and probably understand the implications. Adding Disqus comments doesn’t really scream out that the Facebook SDK will be loaded for all “anonymous” visitors even before they’ve scrolled anywhere near the Disqus comments.&lt;/p&gt;

&lt;p&gt;I’m certain Disqus could fix this, not only resulting in better load times but also better privacy for users. Yes, logging in with Facebook might become slightly slower as a result but this doesn’t seem like a compelling enough reason to keep it as-is to me.&lt;/p&gt;

&lt;p&gt;I don’t think Disqus will change this as a result of my tweet so I figured I’d try and raise some awareness and maybe that would help encourage them.&lt;/p&gt;

&lt;p&gt;Yes, my blog is still using Disqus today. If they don’t fix this then I will probably investigate moving away. I don’t know what good alternatives exist (or how easily I can move all existing comments - which have great value on some posts) so it will take some time if it happens.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2017/01/visiting-a-site-that-uses-disqus-comments-when-not-logged-in-sends-the-url-to-facebook/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/Z19x_2z7lzQ" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/Z19x_2z7lzQ/" title="Visiting a site that uses Disqus comments when not logged in sends the URL to Facebook" />
	<feedburner:origLink>https://blog.dantup.com/2017/01/visiting-a-site-that-uses-disqus-comments-when-not-logged-in-sends-the-url-to-facebook/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-12-02:/2016/12/using-alexa-to-preheat-the-car-and-check-battery-status/</id>
		<published>2016-12-02T00:00:00+00:00</published>
		<updated>2016-12-02T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Amazon Echo" />
		
			<category term="EV" />
		
			<category term="Amazon AWS" />
		
		<title type="text">Using Alexa (Amazon Echo) to Preheat my Renault ZOE and check Battery Status</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Yesterday my &lt;a href="https://amzredir.com/?asinus=B01DFKC2SO&amp;amp;asinuk=B01DFKBL68&amp;amp;tag=dtinfo"&gt;2nd Gen Amazon Echo Dot&lt;/a&gt; arrived (check out &lt;a href="https://www.youtube.com/watch?v=GKI7aKY_hxY"&gt;my kids review of the Echo Dot here&lt;/a&gt;) and I thought it’d be fun to try and hook it up to my &lt;a href="https://www.renault.co.uk/vehicles/new-vehicles/zoe-250.html"&gt;Renault ZOE&lt;/a&gt; so that I could check battery status and preheat it without having to load the clunky app.&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/CyCv6NzrtOI?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;Alexa Skills can be hosted on AWS Lambda and there’s an &lt;a href="https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-lambda-function"&gt;easy-to-follow tutorial in the dev docs&lt;/a&gt;. The Renault ZOE doesn’t have an official API but last year I’d used Fiddler to sniff the mobile app and been able to emulate that. Since then their app has just become a webview wrapper so I was a little worried I wouldn’t be able to do this - however, SPAs to the rescue… the app is written in Angular and calls the backend in a really simple JSON API (way better than the previous XML abomination!).&lt;/p&gt;

&lt;p&gt;I had a quick Google and found that &lt;a href="https://shkspr.mobi/blog/2016/10/reverse-engineering-the-renault-zoe-api/"&gt;Terence Eden had already pasted all of the request/response info online&lt;/a&gt; which saved me a little effort trawling through. I whipped up a quick node script that calls the API to login and request battery status / preheating. It’s relatively simple, not much code at all (though could do with a little refactoring).&lt;/p&gt;

&lt;div class="language-js highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="s2"&gt;"use strict"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zoeUsername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zoePassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"www.services.renault-ze.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"/api"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestData&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="s2"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="s2"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"Bearer "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;};&lt;/span&gt;

	&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to send request &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
			&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
				&lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
			&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;

		&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successful request &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;respData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

		&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="c1"&gt;//console.log("&amp;lt;== " + c.toString());&lt;/span&gt;
			&lt;span class="nx"&gt;respData&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
		&lt;span class="p"&gt;});&lt;/span&gt;
		&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
				&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;respData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;respData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;respData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="p"&gt;});&lt;/span&gt;
	&lt;span class="p"&gt;});&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
	&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nx"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/user/login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zoeUsername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zoePassword&lt;/span&gt;
	&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;loginResponse&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loginResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="nx"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loginResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vehicle_details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
	&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nx"&gt;zoeUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="nx"&gt;zoePassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBatteryStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/vehicle/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"/battery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendPreheatCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/vehicle/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"/air-conditioning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next was wiring this up to work the Alexa API. I created a separate JS file that &lt;code class="highlighter-rouge"&gt;require'd&lt;/code&gt; the one above and just handled two simple intents (&lt;code class="highlighter-rouge"&gt;preheat&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;battery status&lt;/code&gt;). If you ask for help or launch without an intent it just tells you what the options are. Most requests return a card too, so you can see them inside the Alexa app.&lt;/p&gt;

&lt;p&gt;Since we have two cars (two ZOEs!), I actually set this up to respond to two different app IDs and connect to the correct account (and rejected any other apps, so if you jokesters find the ID you can’t burn my battery ;)). The usernames/passwords come from environment variables I would set up in Lambda to make it easier to share code without removing them.&lt;/p&gt;

&lt;div class="language-js highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="s2"&gt;"use strict"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dannyAlexaApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"amzn1.ask.skill.xxxxx"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heatherAlexaApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"amzn1.ask.skill.yyyyy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./car"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shouldEndSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="na"&gt;outputSpeech&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
				&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PlainText"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
				&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="p"&gt;},&lt;/span&gt;
			&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="nx"&gt;shouldEndSession&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Helper to build the text response from battery information.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;buildBatteryStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You have &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charge_level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% battery which will get you approximately &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remaining_range&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.621371&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; miles. `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"The car is plugged in"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;else&lt;/span&gt;
		&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"The car is not plugged in"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;" and charging"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="c1"&gt;// Helper to return a response with a card.		&lt;/span&gt;
	&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Simple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
		&lt;span class="p"&gt;}));&lt;/span&gt;
	&lt;span class="p"&gt;};&lt;/span&gt;
	
	&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event.session.application.applicationId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applicationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="c1"&gt;// Shared callbacks.&lt;/span&gt;
		&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exitCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Goodbye!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
		&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;helpCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buildResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"What would you like to do? You can preheat the car or ask for battery status."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
		&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Authorisation Failure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unable to login to Renault Z.E. Services, please check your login credentials."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="c1"&gt;// Set login based on the Alexa app ID.&lt;/span&gt;
		&lt;span class="c1"&gt;//   We have two cars, so two activation names ("my car" and "Heather's car").&lt;/span&gt;
		&lt;span class="c1"&gt;//   If you're not either of these apps, you're not allowed to control our cars!&lt;/span&gt;
		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applicationId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;dannyAlexaApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZOE_USER_DANNY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZOE_PASS_DANNY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applicationId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;heatherAlexaApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZOE_USER_HEATHER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZOE_PASS_HEATHER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loginFailureCallback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid Application ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You are not allowed to use this service."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
			&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;

		&lt;span class="c1"&gt;// Handle launches without intents by just asking what to do.		&lt;/span&gt;
		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"LaunchRequest"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;helpCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"IntentRequest"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="c1"&gt;// Handle different intents by sending commands to the API and providing callbacks.&lt;/span&gt;
			&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
				&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"PreheatIntent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
					&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendPreheatCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
						&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Car Preheat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"The car will begin preheating shortly."&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
						&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Car Preheat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unable to begin preheating. Have you already done this recently?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
					&lt;span class="p"&gt;);&lt;/span&gt;
					&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
				&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"GetBatteryStatusIntent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
					&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBatteryStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
						&lt;span class="nx"&gt;battery&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Car Battery Status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buildBatteryStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
						&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Car Battery Status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unable to get car battery status, please check your login details."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
					&lt;span class="p"&gt;);&lt;/span&gt;
					&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
				&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"AMAZON.HelpIntent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
					&lt;span class="nx"&gt;helpCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
					&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
				&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"AMAZON.StopIntent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
				&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"AMAZON.CancelIntent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
					&lt;span class="nx"&gt;exitCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
					&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
			&lt;span class="p"&gt;}&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"SessionEndedRequest"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;exitCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Error Occurred"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"An error occurred. Fire the programmer! "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I created two apps in Alexa (both using the same Lambda function) and set up the intent schema:&lt;/p&gt;

&lt;div class="language-js highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"intents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PreheatIntent"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"GetBatteryStatusIntent"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"AMAZON.HelpIntent"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"AMAZON.StopIntent"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;… and some utterances:&lt;/p&gt;

&lt;div class="language-text highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;PreheatIntent pre heat
PreheatIntent preheat
PreheatIntent heat
PreheatIntent heater
PreheatIntent turn the heater on
PreheatIntent warm up
PreheatIntent heat up
PreheatIntent preheat for me
PreheatIntent start the heaters
PreheatIntent turn the heater on
PreheatIntent turn on the heater
PreheatIntent turn the heat on
PreheatIntent turn on the heat
PreheatIntent warm up
PreheatIntent make it toasty for me
PreheatIntent it's cold outside
PreheatIntent heat my seat up
GetBatteryStatusIntent battery status
GetBatteryStatusIntent status
GetBatteryStatusIntent battery
GetBatteryStatusIntent level
GetBatteryStatusIntent battery level
GetBatteryStatusIntent how much range
GetBatteryStatusIntent range
GetBatteryStatusIntent what is your battery status
GetBatteryStatusIntent tell me your status
GetBatteryStatusIntent are you charged
GetBatteryStatusIntent how do your batteries look
GetBatteryStatusIntent what is your battery level
GetBatteryStatusIntent how are your batteries
GetBatteryStatusIntent is there any charge left
GetBatteryStatusIntent how much charge is left
GetBatteryStatusIntent how much range is left
GetBatteryStatusIntent what is your range
GetBatteryStatusIntent how many miles can we drive
GetBatteryStatusIntent what is the range left in the battery
GetBatteryStatusIntent how much range is left in the battery
GetBatteryStatusIntent how far can we drive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, I created a little dummy script that lets me test the functions without having to go through the Echo for a faster dev cycle. I didn’t go as far as mocking the HTTP requests but I did modify the code temporarily to ensure the error handling worked properly (detecting invalid username/password, or if you try to pre-heat too often).&lt;/p&gt;

&lt;div class="language-js highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="s2"&gt;"use strict"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;zoe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./lambda/index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;batteryStatusEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="s2"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="s2"&gt;"application"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="s2"&gt;"applicationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"amzn1.ask.skill.zzz"&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="s2"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="s2"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"IntentRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"GetBatteryStatusIntent"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;preheatEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="s2"&gt;"session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="s2"&gt;"application"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="s2"&gt;"applicationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"amzn1.ask.skill.zzz"&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="s2"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="s2"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"IntentRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="s2"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PreheatIntent"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="na"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;nResult:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;zoe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batteryStatusEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;zoe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preheatEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After some successfully tests, I zipped the files up and uploaded them to Lambda to replace the favourite-color sample I’d been testing with. The final result is in the video at the top of this post!&lt;/p&gt;

&lt;p&gt;You’re free to do as you please with this code. If you improve anything significantly, do leave a comment! If you’ve done anything similar with your &lt;a href="https://amzredir.com/?asinus=B01DFKC2SO&amp;amp;asinuk=B01DFKBL68&amp;amp;tag=dtinfo"&gt;Echo Dot&lt;/a&gt;, do leave a comment!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/12/using-alexa-to-preheat-the-car-and-check-battery-status/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/x8Wpv2_SLCk" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/x8Wpv2_SLCk/" title="Using Alexa (Amazon Echo) to Preheat my Renault ZOE and check Battery Status" />
	<feedburner:origLink>https://blog.dantup.com/2016/12/using-alexa-to-preheat-the-car-and-check-battery-status/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-07-07:/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/</id>
		<published>2016-07-07T00:00:00+00:00</published>
		<updated>2016-07-07T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term=".NET" />
		
		<title type="text">Simplest C# code to post a tweet using OAuth</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: I’ve &lt;a href="/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/"&gt;posted a Dart port of this code here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a few years I’ve been meaning to automate the posting of tweets to a couple of twitter accounts I run. I’ve always been too &lt;s&gt;lazy&lt;/s&gt;busy but this week I had some time and decided to get it done. For many reasons (including that my parents taught me not to take binary dependencies from strangers on the internet ;)) I decided not to use a 5,000 line-of-code third party library but just instead write the simplest code for what I needed.&lt;/p&gt;

&lt;p&gt;While Googling for other peoples implementations of “simplest code to post tweets” to see what I was getting myself into (I didn’t actually find any!)  I stumbled across a page on the Twitter site about &lt;a href="https://dev.twitter.com/oauth/overview/single-user"&gt;Single-user OAuth&lt;/a&gt;. This allows you to generate tokens for authenticating without having to go through the normal OAuth ballache. Obviously you can only do this for your own account but it &lt;em&gt;massively&lt;/em&gt; simplifies things.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;By using a single access token, you don’t need to implement the entire OAuth token acquisition dance. Instead, you can pick up from the point where you are working with an access token to make signed requests for Twitter resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most of the work is fairly straight forward but it also requires an OAuth signature in the OAuth header. This was a bit of a pain in the ass… You have to build a big string of all the data in a specific order with specific encoding and then hash it with the secret keys you got. I implemented this (or so I thought) and sent it off to Twitter only go get a &lt;strong&gt;BAD AUTHENTICATION DATA&lt;/strong&gt; response with zero information on what exactly was wrong. Great :(&lt;/p&gt;

&lt;p&gt;After much keyboard-bashing I ragequit for the night. Stupid Twitter. After some much-required sleep I realised that on the &lt;a href="https://dev.twitter.com/oauth/overview/creating-signatures"&gt;Creating a signature page&lt;/a&gt;, Twitter actually gave an example &lt;em&gt;including the secret keys and timestamp&lt;/em&gt;. This is a perfect test case for testing out hashing code! It even gives the values at each step of the algorithm to help track down where you’re going wrong. My issues turned out to mostly be encoding - I’d tried using &lt;code class="highlighter-rouge"&gt;WebUtility.UrlEncode&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Uri.EscapeUriString&lt;/code&gt; but neither encoded spaces and pluses the way that Twitter excepted. It turned out that &lt;code class="highlighter-rouge"&gt;Uri.EscapeDataString&lt;/code&gt; does encode exactly as Twitter requires.&lt;/p&gt;

&lt;p&gt;In total, my class turned out to be around 80 lines of code without comments. It’s provided here in its entirety for easy copy/pasting (no, there’s no NuGet package.. didn’t anyone tell you that you shouldn’t trust binary dependencies from strangers on the internet?).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Simple class for sending tweets to Twitter using Single-user OAuth.&lt;/span&gt;
&lt;span class="c1"&gt;/// https://dev.twitter.com/oauth/overview/single-user&lt;/span&gt;
&lt;span class="c1"&gt;/// &lt;/span&gt;
&lt;span class="c1"&gt;/// Get your access keys by creating an app at apps.twitter.com then visiting the&lt;/span&gt;
&lt;span class="c1"&gt;/// "Keys and Access Tokens" section for your app. They can be found under the&lt;/span&gt;
&lt;span class="c1"&gt;/// "Your Access Token" heading.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TwitterApi&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TwitterApiBaseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.twitter.com/1.1/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HMACSHA1&lt;/span&gt; &lt;span class="n"&gt;sigHasher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;epochUtc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1970&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTimeKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Utc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

	&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
	&lt;span class="c1"&gt;/// Creates an object for sending tweets to Twitter using Single-user OAuth.&lt;/span&gt;
	&lt;span class="c1"&gt;/// &lt;/span&gt;
	&lt;span class="c1"&gt;/// Get your access keys by creating an app at apps.twitter.com then visiting the&lt;/span&gt;
	&lt;span class="c1"&gt;/// "Keys and Access Tokens" section for your app. They can be found under the&lt;/span&gt;
	&lt;span class="c1"&gt;/// "Your Access Token" heading.&lt;/span&gt;
	&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
	&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TwitterApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumerKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumerKeySecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accessTokenSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

		&lt;span class="n"&gt;sigHasher&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HMACSHA1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ASCIIEncoding&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0}&amp;amp;{1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumerKeySecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessTokenSecret&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;

	&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
	&lt;span class="c1"&gt;/// Sends a tweet with the supplied text and returns the response from the Twitter API.&lt;/span&gt;
	&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
	&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Tweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
			&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"trim_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
		&lt;span class="p"&gt;};&lt;/span&gt;

		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;SendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"statuses/update.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;

	&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TwitterApiBaseUrl&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

		&lt;span class="c1"&gt;// Timestamps are in seconds since 1/1/1970.&lt;/span&gt;
		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;epochUtc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;TotalSeconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="c1"&gt;// Add all the OAuth headers we'll need to use when constructing the hash.&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_consumer_key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_signature_method"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"HMAC-SHA1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_timestamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_nonce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Required, but Twitter doesn't appear to use it, so "a" will do.&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="c1"&gt;// Generate the OAuth signature and add it to our payload.&lt;/span&gt;
		&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_signature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;GenerateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

		&lt;span class="c1"&gt;// Build the OAuth HTTP Header from the data.&lt;/span&gt;
		&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GenerateOAuthHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="c1"&gt;// Build the form data (exclude OAuth stuff that's already in the header).&lt;/span&gt;
		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FormUrlEncodedContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;SendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;

	&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
	&lt;span class="c1"&gt;/// Generate an OAuth signature from OAuth header values.&lt;/span&gt;
	&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
	&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sigString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
			&lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="n"&gt;data&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0}={1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullSigData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
			&lt;span class="s"&gt;"{0}&amp;amp;{1}&amp;amp;{2}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
			&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sigString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
		&lt;span class="p"&gt;);&lt;/span&gt;

		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sigHasher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ComputeHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ASCIIEncoding&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSigData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;())));&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;

	&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
	&lt;span class="c1"&gt;/// Generate the raw OAuth HTML header from the values (including signature).&lt;/span&gt;
	&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
	&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateOAuthHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"OAuth "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
			&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
			&lt;span class="n"&gt;data&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"oauth_"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0}=\"{1}\""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;

	&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
	&lt;span class="c1"&gt;/// Send HTTP Request and return the response.&lt;/span&gt;
	&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
	&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormUrlEncodedContent&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
		&lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oAuthHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

			&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpResp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
			&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;respBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpResp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

			&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;respBody&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
		&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And to use it:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;twitter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TwitterApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConsumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConsumerKeySecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AccessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AccessTokenSecret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is my first automated tweet!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’re free to do as you please with this code. If you improve anything significantly, do leave a comment!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/LOzLyZkeVtY" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/LOzLyZkeVtY/" title="Simplest C# code to post a tweet using OAuth" />
	<feedburner:origLink>https://blog.dantup.com/2016/07/simplest-csharp-code-to-post-a-tweet-using-oauth/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-06-25:/2016/06/html5-pong-in-20min-with-csharp-bridgedotnet/</id>
		<published>2016-06-25T00:00:00+00:00</published>
		<updated>2016-06-25T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Games" />
		
			<category term=".NET" />
		
			<category term="JavaScript" />
		
		<title type="text">HTML5 Pong in 20min with C#/Bridge.NET</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/bridge-pong"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recorded a short (20min) video of creating a simple Pong game in HTML5 using C#/&lt;a href="http://bridge.net/"&gt;Bridge.NET&lt;/a&gt;. It’s not the best Pong game in the world but it is from scratch with no libraries other than Bridge. Maybe useful if you’re thinking of picking up Bridge or building a simple Canvas game.&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/5IVHFX_Q0wA?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;All source code for this project can be found &lt;a href="https://github.com/DanTup/bridge-pong"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Geoffrey McGill from the Bridge team copied this code to deck.net so you can run it live and play around with tweaking the code! &lt;a href="http://deck.net/59eed4385a7f4caee8a0232a63cf72f7"&gt;http://deck.net/59eed4385a7f4caee8a0232a63cf72f7&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/06/html5-pong-in-20min-with-csharp-bridgedotnet/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/lFCud99LDZs" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/lFCud99LDZs/" title="HTML5 Pong in 20min with C#/Bridge.NET" />
	<feedburner:origLink>https://blog.dantup.com/2016/06/html5-pong-in-20min-with-csharp-bridgedotnet/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-06-12:/2016/06/dachip8js-my-csharp-chip8-interpreter-running-in-the-browser/</id>
		<published>2016-06-12T00:00:00+00:00</published>
		<updated>2016-06-12T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Games" />
		
			<category term=".NET" />
		
			<category term="JavaScript" />
		
		<title type="text">DaChip8JS - My C# Chip-8 Interpreter running in the browser</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/DaChip8JS"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yesterday I &lt;a href="/2016/06/building-a-chip-8-interpreter-in-csharp/"&gt;blogged about building DaChip8, my C# Chip-8 interpreter&lt;/a&gt; (I recommend reading that post before this one; the changes detailed here will make more sense if you have an idea of the original code written). It works pretty well but running on the desktop is sooo 2010. It’s 2016, it’s Browser or Bust.&lt;/p&gt;

&lt;p&gt;The problem with the browser is JavaScript. I know lots of you love it, but I don’t. Luckily we have many options for compiling other languages down to JavaScript and the one I happen to have been using recently at work is &lt;a href="http://bridge.net/"&gt;Bridge.NET&lt;/a&gt;. I thought it’d be fun to run my code through it and see what’s involved in making it run in the browser. It turned out to be much less effort than I expected and &lt;a href="https://github.com/DanTup/DaChip8JS/blob/master/DaChip8JS/Chip8.cs"&gt;the main chip implementation&lt;/a&gt; is identical code to &lt;a href="https://github.com/DanTup/DaChip8/blob/master/DaChip8/Chip8.cs"&gt;the C# version&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id="live-demo"&gt;Live Demo&lt;/h2&gt;

&lt;p&gt;Before we go on, here it is. Running in the browser. It works for me in Chrome and Edge but not in IE (no ArrayBuffer support on XmlHttpRequest to fetch the ROM, and possibly more). &lt;strong&gt;It even works on Chrome for Android&lt;/strong&gt; (with touch)! &lt;em&gt;YMMV&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The ROM loaded below is &lt;em&gt;Breakout (Brix hack) [David Winter, 1997]&lt;/em&gt; from the &lt;a href="http://www.chip8.com/?page=109"&gt;Chip-8 Pack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can use the cursor keys to move the paddle on a desktop or tap on the left/right half of the gamescreen on a mobile device.&lt;/p&gt;

&lt;script src="/post_scripts/DaChip8JS.js"&gt;&lt;/script&gt;

&lt;div style="position: relative; width: 640px; max-width: 100%; margin: auto;"&gt;
	&lt;button id="start-dachip8js-game" style="position: absolute; width: 100%; height: 100%; background-color: #000; font-size: 30px; color: #fff;"&gt;Click to Play&lt;/button&gt;
	&lt;canvas id="screen" width="64" height="32" style="width: 640px; image-rendering: pixelated; background-color: #000; cursor: none;"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div id="debug" style="display: none;"&gt;&lt;/div&gt;

&lt;h2 id="video"&gt;Video&lt;/h2&gt;

&lt;p&gt;In case the demo doesn’t work in your browser, here’s a little video (taken well before it was complete, but my internet sucks too much to upload another one!):&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/tDgoDhCumPg?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;h2 id="code-changes"&gt;Code Changes&lt;/h2&gt;

&lt;p&gt;Obviously since rendering on the web is very different to rendering in WinForms I had to make some changes. I also had to work with C# 5 for Bridge (for now). The changes are all external to the Chip8 class (which has hooks to call out for rendering the buffer to screen or playing a beep to keep it platform-agnostic). Below is a summary of the changes I had to make.&lt;/p&gt;

&lt;h3 id="c-5"&gt;C# 5&lt;/h3&gt;

&lt;p&gt;Currently Bridge.NET only supports C# 5 so I had to make a couple of changes for it to compile.&lt;/p&gt;

&lt;p&gt;Expression-bodied methods had to become normal methods:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C# 6&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddXToI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// C# 5&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddXToI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;String interpolation had to be replaced with &lt;code class="highlighter-rouge"&gt;string.Format&lt;/code&gt; calls:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C# 6&lt;/span&gt;
&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Drawing &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-line sprite from &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;startX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;startY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// C# 5&lt;/span&gt;
&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Drawing {0}-line sprite from {1} at {2}, {3}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startY&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="key-and-touch-mappings"&gt;Key and Touch Mappings&lt;/h3&gt;

&lt;p&gt;I had to tweak the key mappings (which previously used the .NET Framework’s &lt;code class="highlighter-rouge"&gt;Keys&lt;/code&gt; enum) to &lt;a href="https://github.com/DanTup/DaChip8JS/blob/master/DaChip8JS/KeyCode.cs"&gt;my own KeyCode enum&lt;/a&gt; which contains the keys required (I added cursor keys here to make the demo ROM better which was missing from the desktop version).&lt;/p&gt;

&lt;h3 id="game-loop-changes"&gt;Game Loop Changes&lt;/h3&gt;

&lt;p&gt;In the C# version my game loop hooked the &lt;code class="highlighter-rouge"&gt;Idle&lt;/code&gt; event of the WinForm and used a timer to tell when it was time to call &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Tick60Hz&lt;/code&gt;. The Bridge version turned out to be much simpler using &lt;code class="highlighter-rouge"&gt;setInterval&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;StartGameLoop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetElapsedTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tick60Hz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetElapsedTime60Hz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Actually, it turned out to be slightly more complicated than that. &lt;code class="highlighter-rouge"&gt;setInterval&lt;/code&gt; has a minimum resolution of 4ms, so I had to tweak this slightly when I realised the game loop wasn’t running as fast as expected (which meant a slower game - Chip-8 games don’t account for their run speed).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HTML5 has minimum resolution of 4ms &lt;/span&gt;
&lt;span class="c1"&gt;// http://developer.mozilla.org/en/DOM/window.setTimeout#Minimum_delay_and_timeout_nesting&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;minimumSetIntervalResolution&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;StartGameLoop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minimumSetIntervalResolution&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tick60Hz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetElapsedTime60Hz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;numTicksToExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minimumSetIntervalResolution&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;targetElapsedTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;numTicksToExecute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
		&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="rom-loading"&gt;ROM Loading&lt;/h3&gt;

&lt;p&gt;In the C# version I just used &lt;code class="highlighter-rouge"&gt;File.ReadAllBytes&lt;/code&gt; to read the ROM from the disk. Obviously I can’t do that in the browser so I used &lt;code class="highlighter-rouge"&gt;XmlHttpRequest&lt;/code&gt; to fetch the ROM. I had to refactor the code a little to allow starting the game when that completed (the C# version loaded synchronously).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="na"&gt;[Ready]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnReady&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="c1"&gt;// ...&lt;/span&gt;

	&lt;span class="c1"&gt;// Kick off async loading of ROM.&lt;/span&gt;
	&lt;span class="nf"&gt;BeginLoadRom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;BeginLoadRom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;rom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
	&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;XMLHttpRequestResponseType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnLoad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;EndLoadRom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;GetResponseAsByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
	&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Convert the response to a byte[] as that's what our existing C# expects.&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;GetResponseAsByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;XMLHttpRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After loading the ROM, only then is it valid to begin the game loop.&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EndLoadRom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

	&lt;span class="nf"&gt;StartGameLoop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="rendering-changes"&gt;Rendering Changes&lt;/h3&gt;

&lt;p&gt;The constructor of my Chip8 class takes an &lt;code class="highlighter-rouge"&gt;Action&amp;lt;bool[,]&amp;gt;&lt;/code&gt; which gets called whenever the screen needs to be rendered. This made it easy to change the rendering code without making changes to Chip8. I used HTML5 canvas to render to:&lt;/p&gt;

&lt;div class="language-html highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"screen"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"min-width: 640px; min-height: 320px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the &lt;code class="highlighter-rouge"&gt;width&lt;/code&gt;/&lt;code class="highlighter-rouge"&gt;height&lt;/code&gt; on a canvas relate to the coordinates being used for drawing and not the actual size in the document. These settings make the canvas ten times bigger than the resolution of the Chip-8.&lt;/p&gt;

&lt;p&gt;I did a bit of Googling to see what’s the fastest way to render pixels to a canvas and found a solution that involves creating &lt;code class="highlighter-rouge"&gt;ImageData&lt;/code&gt; for the pixel then rendering that at each location:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set up canvas rendering.&lt;/span&gt;
&lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetElementById&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"screen"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;screenContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CanvasTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanvasContext2DType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanvasRenderingContext2D&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;lightPixel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;screenContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateImageData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;lightPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set green part (#006400)&lt;/span&gt;
&lt;span class="n"&gt;lightPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Alpha&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

	&lt;span class="c1"&gt;// For performance, we only draw lit pixels so we need to clear the screen first.&lt;/span&gt;
	&lt;span class="n"&gt;screenContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClearRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

	&lt;span class="c1"&gt;// Render each lit pixel.&lt;/span&gt;
	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
		&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
			&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
				&lt;span class="n"&gt;screenContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PutImageData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lightPixel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="sound-changes"&gt;Sound Changes&lt;/h3&gt;

&lt;p&gt;In the WinForms version I used &lt;code class="highlighter-rouge"&gt;Console.Beep&lt;/code&gt;. As with the drawing, I pass an action into the Chip8 constructor for this (an &lt;code class="highlighter-rouge"&gt;Action&amp;lt;int&amp;gt;&lt;/code&gt; this time, withthe int being the duration). The simplest way to play a beep in JS seemed to be with an HTML5 Audio element with a data-url for the beep. I didn’t have control of the duration here, but I figured it’d work fine for now.&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set up audio.&lt;/span&gt;
&lt;span class="c1"&gt;// http://stackoverflow.com/a/23395136/25124&lt;/span&gt;
&lt;span class="n"&gt;beep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HTMLAudioElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data:audio/wav;base64,//uQRAAAAWM (trim a big long string)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Beep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;beep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately I came across an issue when testing this on mobile. You can’t play sounds that weren’t initiated by the user! As a workaround, I made the sound play when you tap to start the emulator.&lt;/p&gt;

&lt;h3 id="touch-support"&gt;Touch Support&lt;/h3&gt;

&lt;p&gt;Since a large portion of traffic to this blog comes from mobile (and I thought having an embedded demo would be pretty cool), I figured I should ensure it works there. I added some touch handlers to the canvas that just emulated pressing the cursor keys based on the side of the canvas that was touched:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set up touch events for canvas so we can play on mobile.&lt;/span&gt;
&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnTouchStart&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;SetTouchDown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnTouchEnd&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;SetTouchUp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetTouchDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TouchEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Touches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;ClientX&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;320&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KeyDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyMapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftCursor&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
	&lt;span class="k"&gt;else&lt;/span&gt;
		&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KeyDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyMapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RightCursor&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
	&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PreventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetTouchUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TouchEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KeyUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyMapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftCursor&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
	&lt;span class="n"&gt;chip8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KeyUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyMapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RightCursor&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
	&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PreventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that was pretty much it!&lt;/p&gt;

&lt;p&gt;All source code for this project can be found &lt;a href="https://github.com/DanTup/DaChip8JS"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/06/dachip8js-my-csharp-chip8-interpreter-running-in-the-browser/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/AQcrMexLZn4" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/AQcrMexLZn4/" title="DaChip8JS - My C# Chip-8 Interpreter running in the browser" />
	<feedburner:origLink>https://blog.dantup.com/2016/06/dachip8js-my-csharp-chip8-interpreter-running-in-the-browser/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-06-11:/2016/06/building-a-chip-8-interpreter-in-csharp/</id>
		<published>2016-06-11T00:00:00+00:00</published>
		<updated>2016-06-11T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Games" />
		
			<category term=".NET" />
		
		<title type="text">Building a Chip-8 Interpreter in C#</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/DaChip8"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Using Bridge.NET, this interpreter can now run in your browser! &lt;a href="/2016/06/dachip8js-my-csharp-chip8-interpreter-running-in-the-browser/"&gt;Click here to see a live playable demo!&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few weeks ago after watching a few episodes of &lt;a href="https://www.youtube.com/playlist?list=PL-sXmdrqqYYcznDg4xwAJWQgNL2gRray2"&gt;Ferris Makes Emulators&lt;/a&gt; where Jake Taylor is live-streaming building an N64 emulator in Rust I decided it might be a fun project to try and build my own. Since I’ve never built an emulator before (and my knowledge of assembly is fairly weak) I thought the NES would be a good choice and although I want to learn some new languages I thought I’d start with C# and once I understand how to actually make an emulator I can build in something like Rust.&lt;/p&gt;

&lt;p&gt;The current code for &lt;a href="https://github.com/DanTup/DaNES"&gt;DaNES is on GitHub&lt;/a&gt; but to cut a long story short, I’m stuck on the rendering. While taking a little break from it I thought it might be wiser to try something simpler like Chip-8. Maybe coming back to the NES with a fresher pair of eyes will make things easier!&lt;/p&gt;

&lt;p&gt;So, the new project is DaChip8 (&lt;a href="https://github.com/DanTup/DaChip8"&gt;also on GitHub&lt;/a&gt;). It’s working and seems to run all the ROMs I’ve tested fine :) Here’s some on how it works for any other newbies that want to give it a shot. Some of the code is a bit messy as I was “learning on the job” but the main implementation of the chip is pretty clean (I think).&lt;/p&gt;

&lt;p&gt;Here’s a video of it in action:&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/O4Jti7J3moY?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;Chip-8 isn’t a physical device like a NES so what we’re building is really an interpreter or VM. Because of this, multiple hardware platforms can(/did) support Chip-8 and games/apps don’t need to be re-written for different hardware. Unfortunately there doesn’t seem to be a defined clock speed for Chip 8 and games were not written to adapt so games will run faster on faster chips. We’ll have to artificially run ours slower because hardware today is so much faster (such as by controlling the rate at which we call &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I implemented my chip as a class in C#. All registers are contained within it and it exposes two methods, &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Tick60Hz&lt;/code&gt; which are controlled externally. &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; is intended to be called at the clock speed (which I’m considering somewhere between 500Hz-1Mhz) and &lt;code class="highlighter-rouge"&gt;Tick60Hz&lt;/code&gt; needs to be called 60 times per second (this is because there are two registers for Delay and Sound which must decrement at this rate).&lt;/p&gt;

&lt;p&gt;Here’s a list of the fields in my &lt;a href="https://github.com/DanTup/DaChip8/blob/master/DaChip8/Chip8.cs"&gt;Chip8 class&lt;/a&gt; and what they are:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Constants for screen size&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ScreenWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ScreenHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// A buffer containing a bool for each pixel on the screen (Chip-8 was monochrome)&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ScreenWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ScreenHeight&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Actions that the chip should invoke (draw will be called from inside Tick60Hz, beep whenever we need to beep).&lt;/span&gt;
&lt;span class="c1"&gt;// These are passed in on the constuctor since they'll generally by implemented in some platform-specific way.&lt;/span&gt;
&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;beep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Chip-8 has 16x 8-bit registers (referred to as V) which we'll store as byte[16].&lt;/span&gt;
&lt;span class="c1"&gt;// As with most things in this file, we'll use hex to refer to things, so these &lt;/span&gt;
&lt;span class="c1"&gt;// are V[0x0] to V[0xF]. The last register, V[F] is also used for special purposes&lt;/span&gt;
&lt;span class="c1"&gt;// such as a carry and collision flag.&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// There are supposed to be two timers (Sound, Delay) but the easiest way for me to implement a beep&lt;/span&gt;
&lt;span class="c1"&gt;// was with Console.Beep(frequency, duration) so rather than have a Sound timer that counts down&lt;/span&gt;
&lt;span class="c1"&gt;// I just immediately start playing for the given duration. This might not be perfect (you can't&lt;/span&gt;
&lt;span class="c1"&gt;// cancel an in-progress sound) but I suspect in reality it works for all games.&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The address register (referred to as I) is 16-bit and is used store memory addresses such as&lt;/span&gt;
&lt;span class="c1"&gt;// when rendering sprites.&lt;/span&gt;
&lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The program counter (where we're currently executing instructions from) is also 16-bit and&lt;/span&gt;
&lt;span class="c1"&gt;// This starts at the location 0x200 (512) because the first 512 bytes were used by the interpreter.&lt;/span&gt;
&lt;span class="c1"&gt;// We use 0x0 to 0x200 to store things like the built-in font data.&lt;/span&gt;
&lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The Chip-8 stack is only used to store program counter locations when jumping to subroutines.&lt;/span&gt;
&lt;span class="c1"&gt;// We use an array that can store 16 addresses and a single byte to index into it. Whenever jumping to&lt;/span&gt;
&lt;span class="c1"&gt;// a subroutine we will push the program counter (PC) to the stack (Stack[SP]) and increment SP&lt;/span&gt;
&lt;span class="c1"&gt;// (the stack pointer). When we need to return, we will decrement SP and then read the address to return&lt;/span&gt;
&lt;span class="c1"&gt;// to from Stack[SP].&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// The original Chip-8 systems had 4K (0x1000) of addressable memory. &lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;RAM&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0x1000&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// To enable fast lookups of functions to execute for each opcode, we use a dictionary.&lt;/span&gt;
&lt;span class="c1"&gt;// Normally we use the first nibble (4 bits) of the two-byte instruction to decide what to do,&lt;/span&gt;
&lt;span class="c1"&gt;// however there's a bunch of random instructions all lumped into the 0xF000 nibble so we have&lt;/span&gt;
&lt;span class="c1"&gt;// an additional dictionary to look them up based on the second byte.&lt;/span&gt;
&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opCodes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opCodesMisc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// One of the instructions is to generate a random number, so we'll need this.&lt;/span&gt;
&lt;span class="n"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;rnd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// We also need to keep track of which keys are currently held down. Chip-8 had a 16-key keyboard&lt;/span&gt;
&lt;span class="c1"&gt;// much like a numeric pad with A-F around the edges:&lt;/span&gt;
&lt;span class="c1"&gt;//    1   2   3   C&lt;/span&gt;
&lt;span class="c1"&gt;//    4   5   6   D&lt;/span&gt;
&lt;span class="c1"&gt;//    7   8   9   E&lt;/span&gt;
&lt;span class="c1"&gt;//    A   0   B   F&lt;/span&gt;
&lt;span class="c1"&gt;// We store these in a HashSet to allow easy checking of whether a key is down.&lt;/span&gt;
&lt;span class="n"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pressedKeys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For building and testing, I used ROMs from the &lt;a href="http://www.chip8.com/?page=109"&gt;Chip-8 Pack&lt;/a&gt;. As far as I can tell these are all games written by hobbyists and freely distributable/usable.&lt;/p&gt;

&lt;p&gt;For loading a ROM, we simply read the bytes from the file and write them into RAM starting at &lt;code class="highlighter-rouge"&gt;0x200&lt;/code&gt; (as explained above):&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The way opcodes are interpreted is generally based on the first nibble (4 bits). There’s a &lt;a href="https://en.wikipedia.org/wiki/CHIP-8#Opcode_table"&gt;great table on Wikipedia&lt;/a&gt; which I won’t duplicate here, but here are a few examples:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;1NNN    Jumps to address NNN.
5XY0    Skips the next instruction if VX equals VY.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where you see &lt;code class="highlighter-rouge"&gt;N&lt;/code&gt;, &lt;code class="highlighter-rouge"&gt;X&lt;/code&gt; or &lt;code class="highlighter-rouge"&gt;Y&lt;/code&gt; in these instructions, those are placeholders. Other values (from &lt;code class="highlighter-rouge"&gt;0&lt;/code&gt; to &lt;code class="highlighter-rouge"&gt;F&lt;/code&gt;) are the bits being matched. The placeholder values also fall into the same pattern, so we can easily assign names to each part of the instruction:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;X = second nibble
Y = third nibble
N = final (fourth) nibble
NN = second byte
NNN = last three nibbles
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make the opcode methods have the same signature, I wrapped this data up in a struct. Now every opcode instruction function just takes an &lt;code class="highlighter-rouge"&gt;OpCodeData&lt;/code&gt; which looks like this:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;OpCodeData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;OpCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;NNN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;NN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we know how opcodes are laid out, we can build our dictionary based on the first nibble (and in the case of the misc instructions, the second byte):&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;opCodes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClearOrReturn&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// There are two instructions starting with a 0 nibble, didn't seem worth its own dictionary&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Jump&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CallSubroutine&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkipIfXEqual&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkipIfXNotEqual&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkipIfXEqualY&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetX&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AddX&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arithmetic&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkipIfXNotEqualY&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetI&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JumpWithOffset&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rnd&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DrawSprite&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkipOnKey&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0xF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Misc&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// This will do a lookup from the second dictionary&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;opCodesMisc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetXToDelay&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x0A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WaitForKey&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetDelay&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetSound&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x1E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AddXToI&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetIForChar&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BinaryCodedDecimal&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SaveX&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0x65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadX&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we need to implement our &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; function which will read a two-byte opcode from RAM at the location the Program Counter (&lt;code class="highlighter-rouge"&gt;PC&lt;/code&gt;) points to then look up the instruction using the first nibble. When implementing this I found there were some instructions in the misc section that we don’t care about so I skipped over them (rather than crashing).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="c1"&gt;// Read the two bytes of OpCode. This is big-endian which means&lt;/span&gt;
	&lt;span class="c1"&gt;// the most significant byte comes first. We shift it 8 bytes to&lt;/span&gt;
	&lt;span class="c1"&gt;// the left then bitwise-or it with the next byte to get the full&lt;/span&gt;
	&lt;span class="c1"&gt;// 16-bit value.&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;RAM&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PC&lt;/span&gt;&lt;span class="p"&gt;++]&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;RAM&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PC&lt;/span&gt;&lt;span class="p"&gt;++]);&lt;/span&gt;

	&lt;span class="c1"&gt;// Write the PC and the OpCode we read for debugging.&lt;/span&gt;
	&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;": "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;opCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X4"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

	&lt;span class="c1"&gt;// Split data into the possible formats the instruction might need.&lt;/span&gt;
	&lt;span class="c1"&gt;// https://en.wikipedia.org/wiki/CHIP-8#Opcode_table&lt;/span&gt;
	&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpCodeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="c1"&gt;// We use bitwise-and with a mask to extract specific nibbles.&lt;/span&gt;
		&lt;span class="n"&gt;OpCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="n"&gt;NNN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x0FFF&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
		&lt;span class="n"&gt;NN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x00FF&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
		&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x000F&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
		&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x0F00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Where don't use the lower nibbles, bitshift right to get just the raw value&lt;/span&gt;
		&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0x00F0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Eg. we want 0x4 not 0x40&lt;/span&gt;
	&lt;span class="p"&gt;};&lt;/span&gt;

	&lt;span class="c1"&gt;// Loop up the OpCode using the first nibble and execute.&lt;/span&gt;
	&lt;span class="n"&gt;opCodes&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;opCode&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)](&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Misc has its own dictionary because it's full of random stuff.&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Misc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opCodesMisc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NN&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
		&lt;span class="n"&gt;opCodesMisc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NN&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, so now we have a function we can tick at 500Hz and it’ll decode an OpCode and call the correct function. Next we need to implement all of those functions!&lt;/p&gt;

&lt;p&gt;I won’t go through all of them here on the blog but I’ll cover the first five from my code. The rest you can find &lt;a href="https://github.com/DanTup/DaChip8/blob/master/DaChip8/Chip8.cs#L168"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first instruction (where the first nibble is &lt;code class="highlighter-rouge"&gt;0x0&lt;/code&gt;) is actually two completely different instructions, one to clear the screen and one to return from a subroutine. There’s no point having another dictionary for these two so we just use a simple if.&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Handles 0x0... which either clears the screen or returns from a subroutine.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ClearOrReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NN&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="c1"&gt;// Our screen buffer is just an array of bools, so set them all to false when&lt;/span&gt;
		&lt;span class="c1"&gt;// instructed to clear the screen.&lt;/span&gt;
		&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ScreenWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
			&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ScreenHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
				&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
	&lt;span class="c1"&gt;// Otherwise we're returning from a subroutine. Here we need to pop the address of the stack&lt;/span&gt;
	&lt;span class="c1"&gt;// and set the program counter to it (we'll cover Pop/Push implementations below).&lt;/span&gt;
	&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NN&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0xEE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two simple jump instructions, &lt;code class="highlighter-rouge"&gt;0x1000&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;0xB000&lt;/code&gt;. One jumps directly to the address from the lower three nibbles (&lt;code class="highlighter-rouge"&gt;NNN&lt;/code&gt;) and the other does the same but adds on the value of the first register (&lt;code class="highlighter-rouge"&gt;V[0]&lt;/code&gt;).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Jumps to location nnn (not a subroutine, so old PC is not pushed to the stack).&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
	&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NNN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Jumps to location nnn + v[0] (not a subroutine, so old PC is not pushed to the stack).&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;JumpWithOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
	&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NNN&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s a third jump instruction which is a “go to subroutine”. This does the same as above but we need to be able to return back to where we were when it finishes executing (it will end with the return instruction covered above). In order to preserve the return address we push the Program Counter (&lt;code class="highlighter-rouge"&gt;PC&lt;/code&gt;) onto the stack before jumping.&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Jumps to subroutine nnn (unlike Jump, this pushes the previous PC to the stack to allow return).&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CallSubroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nf"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
	&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NNN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next OpCode from my code is one that skips the next instruction if &lt;code class="highlighter-rouge"&gt;V[x]&lt;/code&gt; is equal to &lt;code class="highlighter-rouge"&gt;nn&lt;/code&gt; (both &lt;code class="highlighter-rouge"&gt;x&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;nn&lt;/code&gt; are taken from the &lt;code class="highlighter-rouge"&gt;OpCodeData&lt;/code&gt; we decoded). This is how the code branches based on conditions (eg. the next instruction might be a go to subroutine).&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Skips the next instruction (two bytes) if V[x] == nn.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SkipIfXEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpCodeData&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="n"&gt;PC&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Something that was much easier to implement than I expected was the stack. It only holds 16-bit addresses so the implementations of &lt;code class="highlighter-rouge"&gt;Pop&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Push&lt;/code&gt; are incredibly simple:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Pushes a 16-bit value onto the stack, incrementing the SP.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
	&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;++]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Retrieves a 16-bit value from the stack, decrementing the SP.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="nf"&gt;Pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
	&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;[--&lt;/span&gt;&lt;span class="n"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once all instructions are implemented, we need to implement &lt;code class="highlighter-rouge"&gt;Tick60Hz&lt;/code&gt;. All this does is decrement the &lt;code class="highlighter-rouge"&gt;Delay&lt;/code&gt; counter and cause a redraw. The draw function is an &lt;code class="highlighter-rouge"&gt;Action&amp;lt;byte[,]&amp;gt;&lt;/code&gt; supplied in the constructor so it’s up to the consuming code to get that on the screen.&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Tick60Hz&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Delay&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
		&lt;span class="n"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;--;&lt;/span&gt;

	&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we need to something to &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; the chip and draw to the screen. I used a simple WinForms app with a &lt;code class="highlighter-rouge"&gt;PictureBox&lt;/code&gt; which calls &lt;code class="highlighter-rouge"&gt;Tick&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Tick60Hz&lt;/code&gt; at the appropriate times and implements the &lt;code class="highlighter-rouge"&gt;Draw&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;Beep&lt;/code&gt; actions that are passed into the chip like this:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
		&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
			&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetPixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DarkGreen&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Beep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
	&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Beep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s pretty much all there is to it! In addition to the other instructions, there are a few things I didn’t cover here (such as the built-in font, which has sprites for the characters from &lt;code class="highlighter-rouge"&gt;0x0&lt;/code&gt; to &lt;code class="highlighter-rouge"&gt;0xF&lt;/code&gt;) but you can find &lt;a href="https://github.com/DanTup/DaChip8/"&gt;all the code on GitHub&lt;/a&gt; (you’ll need to download the ROMs as mention in the README to run).&lt;/p&gt;

&lt;p&gt;If you’re thinking of building an emulator but unsure where to start, Chip-8 is a great first project. Pretty much everything I’ve done in this one has been the same for the NES emulator I started (with the exception of rendering) so it’s all useful practice.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Using Bridge.NET, this interpreter can now run in your browser! &lt;a href="/2016/06/dachip8js-my-csharp-chip8-interpreter-running-in-the-browser/"&gt;Click here to see a live playable demo!&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/06/building-a-chip-8-interpreter-in-csharp/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/TfDYjRxQ-Hk" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/TfDYjRxQ-Hk/" title="Building a Chip-8 Interpreter in C#" />
	<feedburner:origLink>https://blog.dantup.com/2016/06/building-a-chip-8-interpreter-in-csharp/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-05-29:/2016/05/go-on-a-chromebook-without-linux/</id>
		<published>2016-05-29T00:00:00+00:00</published>
		<updated>2016-05-29T00:00:00+00:00</updated>
		
			<category term="Chromebook" />
		
			<category term="Go" />
		
		<title type="text">Running Go on a Chromebook in Developer Mode (without installing Linux)</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;For the last few years the computer I use most at home has been a &lt;a href="https://www.chromebookchart.com/#cb=dell-chromebook-11"&gt;Dell Chromebook 11&lt;/a&gt;. It’s great for browsing the web but not so great at being a developer machine. Although you can install Linux on it if you wish, that comes with an awful lot of baggage. To avoid using my desktop so much (it’s hard to lock myself away upstairs for hours with a family) I often find myself tryingto get new things running on the Chromebook that I can play around with from the sofa.&lt;/p&gt;

&lt;p&gt;&lt;a href="/2014/09/dart-vm-on-a-chromebook-without-linux/"&gt;Previously it was the Dart VM&lt;/a&gt; and today it is Go. I actually tried Rust and it hasn’t worked out (yet) so Go was a fallback :D&lt;/p&gt;

&lt;p&gt;Note: I’ve never written a line of Go in my life until about 30 minutes ago. This post might not be the best way of doing things and the instructions may become broken in future with Go or ChromeOS changes.&lt;/p&gt;

&lt;p&gt;Setting up Go actually turned out to be pretty simple. Easier than Dart in fact!&lt;/p&gt;

&lt;p&gt;When I did this for Dart, I ended up editing &lt;code class="highlighter-rouge"&gt;.bash_profile&lt;/code&gt; in &lt;code class="highlighter-rouge"&gt;vi&lt;/code&gt; (or using &lt;code class="highlighter-rouge"&gt;echo&lt;/code&gt;) and it was a bit of a clusterfuck. Subsequently I’ve installed &lt;a href="https://chrome.google.com/webstore/detail/text/mmfbcljfglbokpmkimbfghdkjmjhdgbg?hl=en"&gt;Text&lt;/a&gt; and created a symlink at &lt;code class="highlighter-rouge"&gt;~/Downloads/bash_profile&lt;/code&gt; to &lt;code class="highlighter-rouge"&gt;~/.bash_profile&lt;/code&gt; so it can be edited in Text (ChromeOS file manager can only see files inside &lt;code class="highlighter-rouge"&gt;~/Downloads&lt;/code&gt; and not hidden files, hence the missing dot).&lt;/p&gt;

&lt;h2 id="enable-developer-mode"&gt;Enable Developer Mode&lt;/h2&gt;

&lt;p&gt;Instructions on enabling developer mode &lt;a href="https://sites.google.com/site/chromeoswikisite/home/what-s-new-in-dev-and-beta/developer-mode"&gt;can be found here&lt;/a&gt;. Every time you boot your device in this mode you will get a &lt;a href="https://sites.google.com/site/chromeoswikisite/home/what-s-new-in-dev-and-beta/developer-mode"&gt;scary splash screen&lt;/a&gt; and have to press &lt;code class="highlighter-rouge"&gt;Ctrl+D&lt;/code&gt; to avoid resetting your device. &lt;strong&gt;WARNING&lt;/strong&gt;: Enabling developer mode will delete all of your locally stored data (though since it’s a Chromebook, this is really only your Downloads folder).&lt;/p&gt;

&lt;h2 id="download-and-extract-go"&gt;Download and Extract Go&lt;/h2&gt;

&lt;p&gt;Download the &lt;a href="https://golang.org/dl/"&gt;Linux version of Go&lt;/a&gt; to your Downloads folder. I picked the 64 bit AMD version for my &lt;a href="https://www.chromebookchart.com/#cb=dell-chromebook-11"&gt;Dell Chromebook 11&lt;/a&gt;; some Chromebooks might require the 32 bit or ARM version.&lt;/p&gt;

&lt;p&gt;Next, open a terminal window by pressing &lt;code class="highlighter-rouge"&gt;Ctrl+Alt+T&lt;/code&gt; then typing &lt;code class="highlighter-rouge"&gt;shell&lt;/code&gt;. Then extract Go somewhere suitable. I put it at &lt;code class="highlighter-rouge"&gt;~/Coding/go/&lt;/code&gt; (the archived already has everything inside a folder named &lt;code class="highlighter-rouge"&gt;go&lt;/code&gt;).&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;mkdir ~/Coding
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; ~/Downloads/go1.6.2.linux-amd64.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; ~/Coding/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="set-execute-permissions-for-the-file-system"&gt;Set Execute Permissions for the File System&lt;/h2&gt;

&lt;p&gt;In order to execute anyting from this drive you’ll need to remount it as executable. You may have already done this if you’ve set other things up (such as the &lt;a href="/2014/09/dart-vm-on-a-chromebook-without-linux/"&gt;Dart VM&lt;/a&gt;). Without this, you’ll get &lt;code class="highlighter-rouge"&gt;Permission denied&lt;/code&gt; trying to execute anything.&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"sudo mount -i -o remount,exec /home/chronos/user/"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: In order for &lt;code class="highlighter-rouge"&gt;go run&lt;/code&gt; to work you will also need to remount &lt;code class="highlighter-rouge"&gt;/tmp&lt;/code&gt; as executable. &lt;code class="highlighter-rouge"&gt;go run&lt;/code&gt; compiles into a temp folder then executes from there. There may be security implications in doing this!&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"sudo mount -i -o remount,exec /tmp/"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="set-environment-variables"&gt;Set Environment Variables&lt;/h2&gt;

&lt;p&gt;The easiet way to do this is with &lt;a href="https://chrome.google.com/webstore/detail/text/mmfbcljfglbokpmkimbfghdkjmjhdgbg?hl=en"&gt;Text&lt;/a&gt; and a symlink to &lt;code class="highlighter-rouge"&gt;~/.bash_profile&lt;/code&gt; as mentioned before. Otherwise you can try using &lt;code class="highlighter-rouge"&gt;vi&lt;/code&gt; (or &lt;code class="highlighter-rouge"&gt;echo&lt;/code&gt;)!&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Coding/go &lt;span class="c"&gt;# This is where you extracted Go&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Downloads/go &lt;span class="c"&gt;# This is where you will keep your Go projects&lt;/span&gt;
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:&lt;span class="nv"&gt;$GOROOT&lt;/span&gt;/bin:&lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/bin &lt;span class="c"&gt;# This adds both Go and your "installed" projects to PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="restart-and-test"&gt;Restart and Test&lt;/h2&gt;

&lt;p&gt;Type &lt;code class="highlighter-rouge"&gt;exit&lt;/code&gt; or close the terminal window then re-open with &lt;code class="highlighter-rouge"&gt;Ctrl+Alt+T&lt;/code&gt; and type &lt;code class="highlighter-rouge"&gt;shell&lt;/code&gt;. Type &lt;code class="highlighter-rouge"&gt;go version&lt;/code&gt; and you should see &lt;code class="highlighter-rouge"&gt;go version go1.6.2 linux/amd64&lt;/code&gt; or similar.&lt;/p&gt;

&lt;h2 id="hello-world"&gt;Hello, World!&lt;/h2&gt;

&lt;p&gt;Now we have a working Go, we should build &lt;a href="https://golang.org/doc/install#testing"&gt;Hello World&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;There seems to be a convention that you should name your packages after the repo you’ll be hosting them in. They’ll need to hang off &lt;code class="highlighter-rouge"&gt;$GOPATH/src&lt;/code&gt;, eg:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;mkdir &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/github.com/&lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;/hello &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next create a &lt;code class="highlighter-rouge"&gt;hello.go&lt;/code&gt; file in that directory using your favourite editor with a simple Hello World:&lt;/p&gt;

&lt;div class="language-go highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="x"&gt;

&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;&lt;span class="x"&gt;

&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="x"&gt;
	&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="x"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="x"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we want to build and execute it. If we use &lt;code class="highlighter-rouge"&gt;go install&lt;/code&gt; instead of &lt;code class="highlighter-rouge"&gt;go build&lt;/code&gt; it will copy the executable into &lt;code class="highlighter-rouge"&gt;$GOPATH/bin&lt;/code&gt; which we added to our &lt;code class="highlighter-rouge"&gt;PATH&lt;/code&gt; earlier. We can either pass the package path (relative to &lt;code class="highlighter-rouge"&gt;$GOPATH&lt;/code&gt;) to &lt;code class="highlighter-rouge"&gt;go install&lt;/code&gt; or change to the directory and omit it:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/github.com/&lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;/hello
go install
hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you chose to remount &lt;code class="highlighter-rouge"&gt;/tmp&lt;/code&gt; as executable then you can also use &lt;code class="highlighter-rouge"&gt;go run {file}&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/github.com/&lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;/hello
go run hello.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’ve done everything correctly, this will output &lt;code class="highlighter-rouge"&gt;Hello, World!&lt;/code&gt; :)&lt;/p&gt;

&lt;p&gt;If you’re interested in a Chromebook; don’t forget to take a look at &lt;a href="https://www.chromebookchart.com/"&gt;Chromebook Comparison Chart&lt;/a&gt;! :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/05/go-on-a-chromebook-without-linux/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/GklB87RA6ZA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/GklB87RA6ZA/" title="Running Go on a Chromebook in Developer Mode (without installing Linux)" />
	<feedburner:origLink>https://blog.dantup.com/2016/05/go-on-a-chromebook-without-linux/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-05-08:/2016/05/easy-javascript-and-css-bundling-on-github-pages-without-build-steps/</id>
		<published>2016-05-08T00:00:00+00:00</published>
		<updated>2016-05-08T00:00:00+00:00</updated>
		
			<category term="GitHub" />
		
			<category term="JavaScript" />
		
		<title type="text">Easy JavaScript and CSS bundling and minification on GitHub Pages without build steps</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Recently we were tidying up the design a little on &lt;a href="https://www.chromebookchart.com/"&gt;chromebookchart.com&lt;/a&gt; and trying to make it work better on mobile. While setting up Google Analytics and Google Webmaster Tools I somehow found myself at Google PageSpeed and trying to improve our score there and in &lt;a href="https://developer.chrome.com/devtools#audits"&gt;Google Chrome’s Audit tab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of the issues resolved around external files. We had quite a few of them (thanks to Google MDL) and the caching and &lt;a href="https://github.com/google/material-design-lite/issues/4319"&gt;gzip wasn’t working&lt;/a&gt; on some (there’s something ironic about a Google project serving up uncompressed files to Chrome). To fix this I decided we should just download the Google Material Design Lite files locally (they don’t change, they’re versioned). CloudFlare lets me control the caching (and enables gzip) on these better than we were getting from Google. But, this meant more files being served from our domain and not bundling them together seemed a bit silly.&lt;/p&gt;

&lt;p&gt;The site is hosted free using &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt; (run through &lt;a href="https://www.cloudflare.com/"&gt;CloudFlare&lt;/a&gt; to get free HTTPS). We have a small C# app which rips data from the &lt;a href="https://docs.google.com/spreadsheets/d/1JvRtWh8ixWeYoOGu7oZ3fpw53eKfzsTWKp_XlWKofNs/edit"&gt;public spreadsheet&lt;/a&gt; and spits it out into Jekyll-formatted files for display but otherwis it’s entirely static. &lt;a href="https://support.cloudflare.com/hc/en-us/articles/200168196-How-do-I-minify-HTML-CSS-and-JavaScript-to-optimize-my-site-"&gt;CloudFlare is already minifying&lt;/a&gt; the content (though in this case, this third party code is already pre-minified) so I just wanted the simplest possible way to bundle all the scripts together in one file that didn’t not require us to run any scripts whenever we changed a file (sometimes we tweak things directly through the GitHub web app on Chromebooks where it’s more coplicated to run scripts and push to GitHub).&lt;/p&gt;

&lt;p&gt;Turned out, I already had the solution on this blog. My Google Analytics script is included using Jekyll includes like this:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;{% include analytics.htm %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So I thought I’d give it a try with JavaScript. I created a file named &lt;code class="highlighter-rouge"&gt;bundle.js&lt;/code&gt; inside my &lt;code class="highlighter-rouge"&gt;s/js&lt;/code&gt; folder and added the following:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;{% include material.min.js %}
{% include jquery-1.11.1.min.js %}
{% include jquery.tablesorter.min.js %}
{% include jquery.tablesorter.widgets.js %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I pushed to GitHub and navigated to &lt;code class="highlighter-rouge"&gt;/s/js/bundle.js&lt;/code&gt; in my browser. It came through completely untransformed. Doh.&lt;/p&gt;

&lt;p&gt;I did some digging around to try and understand why this wouldn’t work and discovered (or rather re-remembered) that you need &lt;a href="https://jekyllrb.com/docs/frontmatter/"&gt;YAML front-matter&lt;/a&gt; for a page to be transformed in Jekyll. So I tried again:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;---
---
{% include material.min.js %}
{% include jquery-1.11.1.min.js %}
{% include jquery.tablesorter.min.js %}
{% include jquery.tablesorter.widgets.js %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After much refreshing, nothing changed. I was about to give up when I noticed an email from GitHub:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The page build failed with the following error:&lt;/p&gt;

  &lt;p&gt;A file was included in &lt;code class="highlighter-rouge"&gt;s/js/bundle.js&lt;/code&gt; that is a symlink or does not exist in your &lt;code class="highlighter-rouge"&gt;_includes&lt;/code&gt; directory. For more information, see &amp;gt; https://help.github.com/articles/page-build-failed-file-is-a-symlink.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whoops! Includes normally live in &lt;code class="highlighter-rouge"&gt;_includes&lt;/code&gt; so I need to use &lt;code class="highlighter-rouge"&gt;include_relative&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;---
---
{% include_relative material.min.js %}
{% include_relative jquery-1.11.1.min.js %}
{% include_relative jquery.tablesorter.min.js %}
{% include_relative jquery.tablesorter.widgets.js %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After pushing this, everything came through as expected. Now all these files are combined into one file and CloudFlare is minifying them. One request to the same domain as the website is much better than four requests that include an additional uncommon domain.&lt;/p&gt;

&lt;p&gt;Via CloudFlare, this bundle is set to cache in browsers for one month. This makes the site nice and fast but will also impact the ability to make changes. For this reason our own JavaScript (&lt;code class="highlighter-rouge"&gt;/js/main.js&lt;/code&gt;) is neither included in this bundle nor in the &lt;code class="highlighter-rouge"&gt;/s/&lt;/code&gt; folder (anything in &lt;code class="highlighter-rouge"&gt;/s/&lt;/code&gt; gets the 1-month-cache header).&lt;/p&gt;

&lt;p&gt;There’s still lots of room for improvement (like the CSS) but it’s a good start and &lt;a href="https://www.chromebookchart.com/"&gt;chromebookchart.com&lt;/a&gt; seems to load very quickly already!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/05/easy-javascript-and-css-bundling-on-github-pages-without-build-steps/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/-7swQ0A1v5Q" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/-7swQ0A1v5Q/" title="Easy JavaScript and CSS bundling and minification on GitHub Pages without build steps" />
	<feedburner:origLink>https://blog.dantup.com/2016/05/easy-javascript-and-css-bundling-on-github-pages-without-build-steps/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-04-26:/2016/04/have-software-developers-given-up/</id>
		<published>2016-04-26T00:00:00+00:00</published>
		<updated>2016-04-26T00:00:00+00:00</updated>
		
			<category term="Visual Studio" />
		
			<category term="Linux" />
		
			<category term="Android" />
		
		<title type="text">Have Software Developers Given Up?</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;style type="text/css"&gt;
.post-content img {
    margin: 0 10px 10px 30px;
    border: solid 1px #ddd;
    max-width: 90%;
}
&lt;/style&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: I’ve been finding so many software failures lately, I created &lt;a href="https://twitter.com/SoftwareFailed"&gt;@SoftwareFailed&lt;/a&gt; on Twitter to tweet and retweet the best failures!&lt;/p&gt;

&lt;p&gt;Note: I’m a software developer. I create bugs. I once switched a production SQL database to Simple recovery mode and Truncated an important table causing a ton of work for my colleagues. The content of this post is aimed as much at me and the company I work for as the companies listed here. I think our industry has a real quality problem. I don’t entirely know what the solution is.&lt;/p&gt;

&lt;p&gt;Over the last few years it feels like the quality of software and services across the industry is falling rather than climbing. Everything is always beta (both in name and quality). Things are shipped when marketing wants them to rather than when they’re ready because “we can easily patch them”. End users have basically become testers, but it’s ok, because this is Agile. We’ve started coding to expect failure and somehow with it decided that failure is normal and expected and we don’t need to put so much effort into avoiding it. Supporting millions of customers is complicated so we don’t bother. Why waste time reading bug reports from users when you can just send them into an endless maze of help links with no contact information?&lt;/p&gt;

&lt;p&gt;I never used to be this grumpy. The last few years I’ve seen so many ridiculous errors in software and on websites that I just can’t help but feel a little embarassed about what we (as software devs) are unleashing on the world. I know we’re a young, inexperienced industry and that there aren’t enough skilled devs to go around but lately it feels like we’re really not even trying.&lt;/p&gt;

&lt;p&gt;Here’s a collection of some screenshots I’ve taken &lt;strong&gt;just in the last month&lt;/strong&gt; showing what I mean. Is it just me? Am I really unlucky? Or does this happen to everyone and it’s just me that likes to put effort into being vocal and annoyed by it?&lt;/p&gt;

&lt;p&gt;It’s not unusual to see data that’s unescaped or badly encoded, but it’s not often you see things over-encoded. NPM package names appear to be HTML encoded twice! Let’s hope the first one wasn’t as it was saved into the database :/&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/npm_encoding_fail.jpg" alt="If in doubt, encode it again!" /&gt;&lt;/p&gt;

&lt;p&gt;While investigating an issue for a friend, I found a page on the ASUS site where the title shown in Google perfectly matched our problem. I clicked through and the page was blank, except for this form asking if it solved my problem.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/asus_missing_content.jpg" alt="Did this blank page solved you problem?" /&gt;&lt;/p&gt;

&lt;p&gt;IKEA emailed me reminding me I hadn’t completed a survey that I had. I figured I hadn’t clicked save. I clicked to complete it again and was told I’d already filled it in.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/bad_reminder.jpg" alt="Accusation of not completing a survey that was completed the day it was sent!" /&gt;&lt;/p&gt;

&lt;p&gt;I got an email today from Coursera with an unreplaced replacement token in the subject.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/coursera.jpg" alt="Unreplaced token in email subject" /&gt;&lt;/p&gt;

&lt;p&gt;Our company anti-virus for the &lt;em&gt;second time in less than a month&lt;/em&gt; blocked access to most of the internet. Twitter was full of people having the same issue. I find this truly astonishing. Do they not install updates on a machine and give them a quick test before pushing out to the world? Large sites like the BBC and Google were blocked!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/eset_killed_internet.jpg" alt="ESET antivirus killed half of the internet AGAIN" /&gt;&lt;/p&gt;

&lt;p&gt;Not sure what to make of this message when shutting my Raspberry Pi down!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/failed_or_succeeded.jpg" alt="Succeeded to fail!" /&gt;&lt;/p&gt;

&lt;p&gt;Tried to visit the HTC Vive website and got a MongoDB connection error spewed to the screen. Reported it on Twitter and quickly got a witty response; but it remained broken until the next day.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/htc_vive.jpg" alt="New product website down over the weekend, technical details on display" /&gt;&lt;/p&gt;

&lt;p&gt;I tried setting up Ubuntu MATE on my Pi. I wrote their (Pi-specific) image to my SD card and this happened on first boot. Something wasn’t tested; whether it’s the image or some dependency being pulled from the web at first boot, I’m not sure.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/mate_install_fail.jpg" alt="Clean operating system image fails to start up" /&gt;&lt;/p&gt;

&lt;p&gt;I was trying to download IIS Log Parser today but the download didn’t seem to work. I opened the dev tools to find “jQuery not defined” errors all over the place. Persisted across refreshes.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/microsoft_jquery_fail.jpg" alt="Microsoft website non-functional apparently due to missing jQuery" /&gt;&lt;/p&gt;

&lt;p&gt;I tried to move my gas and electric to NPower. They sent me a welcome email and when I clicked the button in it I got a big scary SSL warning from my browser. It turned out to be a wildcard certificate (*.npower.com) but the emails links point to https://npower.com/. I reported this to at least 5 different people there, yet nobody seemed to take it seriously (or even understand it, as shown here). I certainly wouldn’t want to show my new customers browser warnings that say attackers may be trying to steal their information!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/npower_ssl_issue.jpg" alt="Security is overrated" /&gt;&lt;/p&gt;

&lt;p&gt;One security issue on your site is bad enough; but come on, the live chat has a different certificate error!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/npower_chat_ssl.jpg" alt="If we don't have working SSL on our website, why would we have it on the Live Chat?" /&gt;&lt;/p&gt;

&lt;p&gt;Yesterday I tried to checkout with Paypal and after logging in I got this message. It’s bad enough to get an error during payment for something, but this message is particularly useless. There’s no information to help me. No support phone number, no indication of whether I was charged or what I should do next.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/paypal.jpg" alt="Did you just charge my card or not?" /&gt;&lt;/p&gt;

&lt;p&gt;Many years ago, Microsoft took a lot of flak for podcasts only working on Windows Phone in the US. Fast forward a few years and it seems Google want in. I see no good reason for this; they’re free audio files. All of the other Google Play Music services work in the UK, so really, what’s the reason for this?&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/podcasts_country.jpg" alt="Audio is country-specific..." /&gt;&lt;/p&gt;

&lt;p&gt;Podomatic helpfully prefix your iTunes URL with “http://”. This happens even if you already have “https://” (which, by the way, is the correct protocol for an iTunes link - HTTP gets redirected automatically). The only solution is to manually edit the URL to be HTTP before saving, even though that’s wrong.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/podomatic_forced_http.jpg" alt="Are you sure you didn't mean HTTP?" /&gt;&lt;/p&gt;

&lt;p&gt;This one is amazing. Someone signed up my email address to a service using the name “Pro_Hacking”. I reported it because I was concerned that maybe their service was not very secure and someone might end up with an account tied to my email. It’s hard to believe the support person was even reading the emails the number of times I explained “Pro_Hacking” is not my name, to get a response starting “Hello Pro-Hacking”!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/server_pro_hacking.jpg" alt="Hello, Pro_Hacking!" /&gt;&lt;/p&gt;

&lt;p&gt;The move of our gas and electric to NPower didn’t work out. It turns out that for some “mysterious technical reason” they are unable to take over supplies from the company that (physically) supply our gas. So we arranged to move to SWALEC. Weeks on, we’ve still had no code mailed to us and are therefore unable to log in to their website. The contact link on the only page we can access goes to an error page.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/swalec_contact_fail.jpg" alt="If you're frustrated we didn't do our job, here's an error page!" /&gt;&lt;/p&gt;

&lt;p&gt;This website accused me of using an ad blocker and wouldn’t let me read this article. Quite clearly from the screenshot, I am not using an AdBlocker. I completely appreciate the need to monetise content (which is why I’ve been putting off installing an Ad Blocker) but if sites are going to add code like this it should fail-safe. Accusing me of stealing from your children when I’m innocent is just going to drive me away!&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/v3_adblock.jpg" alt="Website refuses to let me read articles because it claims I'm using an AdBlocker (if I am, it's not very effective)" /&gt;&lt;/p&gt;

&lt;p&gt;I tried setting up VNC on my Pi. It’s behind a firewall so I entered a short password. I was told it was too short, so I added a few characters. I was told it was truncated to EIGHT characters.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/vnc_passwords.jpg" alt="Short passwords are not secure. Long passwords are too secure" /&gt;&lt;/p&gt;

&lt;p&gt;When installing a Visual Studio update, it told me the Visual Studio Improvement Program was optional. I’m pretty sure I opted-out at install time; however this time it was both ticked and disabled (yet still sporting the “Optional” tag).&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/vs_optional_but_not.jpg" alt="I think we have different definitions of the word Optional" /&gt;&lt;/p&gt;

&lt;p&gt;Immediately after installing Visual Studio 2015 Update 2 (which claimed to fix a bunch of stability issues), I launched Visual Studio and it just crashed. I don’t believe I have any non-Microsoft extensions installed.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/vs_update_2.jpg" alt="Update installed! *crash*" /&gt;&lt;/p&gt;

&lt;p&gt;Every few weeks without me doing anything, my Moto 360 Android Wear watch will just randomly start draining it’s battery until it’s flat/rebooted. I reported this to Google once and was told “this is expected behaviour after updates”. I hadn’t had any updates, but even so, that would be stupid.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/fails/wear_drain.jpg" alt="???" /&gt;&lt;/p&gt;

&lt;p&gt;While preparing this post I went looking for a command line tool to optimise the images in this post. I came across &lt;a href="http://www.creativebloq.com/design/image-compression-tools-1132865"&gt;this post&lt;/a&gt; and every time I tried to scroll down the page, it immediately jumped back up to the top. After about 40 seconds a full-page ad appeared, which appears to have been the cause. &lt;em&gt;sigh&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That’s a lot of fail for just one month, and I’m sure there’s a lot more I didn’t have screenshots of. I can’t help but feel that as an industry we’re just not doing our best for our users. Even companies that have previously been known for exceptional quality and testing seem to have gone down the shitter. I’m no stranger to commercial pressures causing things to be shipped before they’re done, but surely there’s room for improvement?&lt;/p&gt;

&lt;p&gt;Or maybe it’s the end users fault? Maybe we don’t complain enough when things are bad, so companies have little motivation to improve?&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/04/have-software-developers-given-up/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/kf1v4OcvGnc" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/kf1v4OcvGnc/" title="Have Software Developers Given Up?" />
	<feedburner:origLink>https://blog.dantup.com/2016/04/have-software-developers-given-up/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-04-23:/2016/04/removing-rsyslog-spam-on-raspberry-pi-raspbian-jessie/</id>
		<published>2016-04-23T00:00:00+00:00</published>
		<updated>2016-04-23T00:00:00+00:00</updated>
		
			<category term="Raspberry Pi" />
		
			<category term="Linux" />
		
		<title type="text">Removing [action 'action 17' suspended] rsyslog Spam on Raspberry Pi (Raspian Jessie)</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;As a bit of a Linux noob, I’ve been keeping an eye on the logs for my &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt; to ensure I haven’t set anything up wrong. I noticed that quite frequently my syslog contains this sort of junk:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;Apr  2 01:23:51 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:24:21 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:29:41 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:30:11 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:31:35 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:32:05 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:35:52 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:36:22 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:39:06 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:40:06 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:44:39 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:45:39 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:53:08 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 01:54:08 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
Apr  2 01:59:41 raspberrypi rsyslogd-2007: action &lt;span class="s1"&gt;'action 17'&lt;/span&gt; suspended, next retry is Sat Apr  2 02:00:41 2016 &lt;span class="o"&gt;[&lt;/span&gt;try http://www.rsyslog.com/e/2007 &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As well as being annoying when scanning through logs, it also doesn’t really help the life of the SD card on a Pi if things are churning data out needlessly. It turns out to be caused by rsyslog trying to write to /dev/xconsole and for whatever reason, failing. The &lt;code class="highlighter-rouge"&gt;rsyslog.conf&lt;/code&gt; file in Raspbian Jessie contains this at the end:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,&lt;/span&gt;
&lt;span class="c"&gt;# you must invoke `xconsole' with the `-file' option:&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;#    $ xconsole -file /dev/xconsole [...]&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# NOTE: adjust the list below, or you'll go crazy if you have a reasonably&lt;/span&gt;
&lt;span class="c"&gt;#      busy site..&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
daemon.&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;mail.&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    news.err&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="o"&gt;=&lt;/span&gt;debug&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="o"&gt;=&lt;/span&gt;info&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="o"&gt;=&lt;/span&gt;notice&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="o"&gt;=&lt;/span&gt;warn   |/dev/xconsole
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The fix is simple enough - just remove those last lines. I’ve been trying to script all of the setup of my Pi so I can easily rebuild it if (when) I trash things or to try things out on a new SD card.&lt;/p&gt;

&lt;p&gt;After much Googling, I settled on this command to do it for me:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/# The named pipe \/dev\/xconsole/,$d'&lt;/span&gt; /etc/rsyslog.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This says match all lines starting with the one that matches &lt;code class="highlighter-rouge"&gt;# The named pipe /dev/console&lt;/code&gt; and ending with the last line (&lt;code class="highlighter-rouge"&gt;$&lt;/code&gt;) and delete them. If you have any additional lines at the end of your file you’ll need to tweak this, but that seems unlikely (most things are likely to write into &lt;code class="highlighter-rouge"&gt;/etc/rsyslog.d/&lt;/code&gt;. I don’t know if you need to restart rsyslog after this; I rebooted just for fun.&lt;/p&gt;

&lt;p&gt;Hope this is helpful. If you have any problems, leave a comment. Bear in mind I’m a Linux noob and what’s written above might not be the best way to achieve this and I take no responsibility if anything breaks :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/04/removing-rsyslog-spam-on-raspberry-pi-raspbian-jessie/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/pisgynMChek" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/pisgynMChek/" title="Removing [action 'action 17' suspended] rsyslog Spam on Raspberry Pi (Raspian Jessie)" />
	<feedburner:origLink>https://blog.dantup.com/2016/04/removing-rsyslog-spam-on-raspberry-pi-raspbian-jessie/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-04-09:/2016/04/setting-up-automatic-updates-on-raspberry-pi-raspbian-jessie/</id>
		<published>2016-04-09T00:00:00+00:00</published>
		<updated>2016-04-09T00:00:00+00:00</updated>
		
			<category term="Raspberry Pi" />
		
			<category term="Linux" />
		
		<title type="text">Setting up Automatic Updates on Raspberry Pi (Raspian Jessie)</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Logging into machines and installing security updates periodically isn’t fun but for internet-exposed devices it’s important. Any device that’s on your home network has the possibility of being a stepping stone for attackers if it can be easily breached.&lt;/p&gt;

&lt;p&gt;The first thing to do before setting up automatic updates is to ensure your &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt; can send email. You’ll want to know when updates are being installed (or if they fail). My &lt;a href="/2016/04/setting-up-raspberry-pi-raspbian-jessie-to-send-email/"&gt;previous blog post&lt;/a&gt; covers how to do this with Postfix.&lt;/p&gt;

&lt;p&gt;As always, start off by making sure your apt list and existing packages are up-to-date:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="c"&gt;# Update the package list, update all packages and remove any packages that are no longer required&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we need to install the &lt;code class="highlighter-rouge"&gt;unattended-upgrades&lt;/code&gt; package and to ensure it sends emails the &lt;code class="highlighter-rouge"&gt;apt-listchanges&lt;/code&gt; package. &lt;code class="highlighter-rouge"&gt;apt-listchanges&lt;/code&gt; also requires a &lt;code class="highlighter-rouge"&gt;mailx&lt;/code&gt; program so if you don’t already have one you can grab &lt;code class="highlighter-rouge"&gt;bsd-mailx&lt;/code&gt;:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get install unattended-upgrades apt-listchanges bsd-mailx &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we should configure where the updates are allowed to come from.If you choose to stick with Stable then when the next version of Raspbian goes stable (Stretch) it’ll automatically update. I’ve decided to stick with Jessie for now. This config lives in &lt;code class="highlighter-rouge"&gt;/etc/apt/apt.conf.d/50unattended-upgrades&lt;/code&gt; and you can use this script to uncomment the line for Jessie.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^\/\/      "o=Raspbian,n=jessie"/      "o=Raspbian,n=jessie"/g'&lt;/span&gt; /etc/apt/apt.conf.d/50unattended-upgrades&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we want to instruct the updater to send emails. Again, this is already in the config file so it’s just a case of uncommenting it (you may wish to tweak the user, but I’ve &lt;a href="/2016/04/setting-up-raspberry-pi-raspbian-jessie-to-send-email/"&gt;already set&lt;/a&gt; &lt;code class="highlighter-rouge"&gt;root&lt;/code&gt; mail to be forwarded on to my user).&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^\/\/Unattended-Upgrade::Mail "root";/Unattended-Upgrade::Mail "root";/g'&lt;/span&gt; /etc/apt/apt.conf.d/50unattended-upgrades&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;By default your Pi won’t be rebooted if required, so if you want it to (and want to set the time) you can do that like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^\/\/Unattended-Upgrade::Automatic-Reboot "false";/Unattended-Upgrade::Automatic-Reboot "true";/g'&lt;/span&gt; /etc/apt/apt.conf.d/50unattended-upgrades&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^\/\/Unattended-Upgrade::Automatic-Reboot-Time "02:00";/Unattended-Upgrade::Automatic-Reboot-Time "02:00";/g'&lt;/span&gt; /etc/apt/apt.conf.d/50unattended-upgrades&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And if if you want unused packages to be removed (like when you run &lt;code class="highlighter-rouge"&gt;apt-get autoremove&lt;/code&gt;:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sed &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^\/\/Unattended-Upgrade::Remove-Unused-Dependencies "false";/Unattended-Upgrade::Remove-Unused-Dependencies "true";/g'&lt;/span&gt; /etc/apt/apt.conf.d/50unattended-upgrades&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we must create the &lt;code class="highlighter-rouge"&gt;/etc/apt/apt.conf.d/20auto-upgrades&lt;/code&gt; file to instruct the updater what to do:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="c"&gt;# You could also create this file by running "dpkg-reconfigure -plow unattended-upgrades"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tee /etc/apt/apt.conf.d/20auto-upgrades &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;APT::Periodic::Update-Package-Lists "1";&lt;br data-jekyll-commonmark-ghpages="" /&gt;APT::Periodic::Unattended-Upgrade "1";&lt;br data-jekyll-commonmark-ghpages="" /&gt;EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And that’s all there is to it! Every day your Pi will now check for updates and you’ll receive an email like this if there were:&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/pi_updates.jpg" alt="Updates email from Raspberry Pi" /&gt;&lt;/p&gt;

&lt;p&gt;If you want to chek it’s working, you can check the log file tomorrow:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /var/log/unattended-upgrades/unattended-upgrades.log&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hope this is helpful. If you have any problems, leave a comment. Bear in mind I’m a Linux noob and what’s written above might not be the best way to achieve this and I take no responsibility if anything breaks :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/04/setting-up-automatic-updates-on-raspberry-pi-raspbian-jessie/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/-7KeAJeMhKw" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/-7KeAJeMhKw/" title="Setting up Automatic Updates on Raspberry Pi (Raspian Jessie)" />
	<feedburner:origLink>https://blog.dantup.com/2016/04/setting-up-automatic-updates-on-raspberry-pi-raspbian-jessie/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-04-08:/2016/04/setting-up-raspberry-pi-raspbian-jessie-to-send-email/</id>
		<published>2016-04-08T00:00:00+00:00</published>
		<updated>2016-04-08T00:00:00+00:00</updated>
		
			<category term="Raspberry Pi" />
		
			<category term="Linux" />
		
			<category term="Email" />
		
		<title type="text">Setting up a Raspberry Pi (Raspian Jessie) to send Email</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;There are a bunch of things on Linux that send emails by default (for example, the output of Cron jobs). Being a Linux noob I wanted to ensure I received these emails (what if something goes bad because of my noobness?) and also wanted to ensure my &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt; can send emails to me from scripts if required in the future.&lt;/p&gt;

&lt;p&gt;There are a lot of tutorials online about how to do this most of which &lt;strong&gt;require hard-coding your email account password on your Pi&lt;/strong&gt;. I’m not ready for this sort of commitment (even if it’s an App-Specific password) and knowing a little about SMTP I was fairly sure it wasn’t required if I was only deliverying mail to one place.&lt;/p&gt;

&lt;p&gt;So, the things we need to do are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set up all mail to be passed on to the same local account (in my case &lt;code class="highlighter-rouge"&gt;danny&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Set up that accounts mail to forward to an external address (a real email address, mine being hosted by Google)&lt;/li&gt;
  &lt;li&gt;Install and configure an MTA that can actually connect to the external SMTP server and deliver the email&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of these seemed pretty straight forward, until I actually came to try… It turns out that if you’re deliverying email to Google and your ISP supports IPv6 (mine does) and you don’t want to disable IPv6 (I do not) then you will find yourself receiving errors like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our system has detected that this message does not meet IPv6 sending guidelines regarding PTR records and authentication. Please review https://support.google.com/mail/?p=ipv6_authentication_error for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For some reason Google have added some extra restrictions on being able to talk to their SMTP servers over IPv6, some of which you likely can’t solve (because the IP address is owned by your ISP and not you). &lt;a href="http://tanguy.ortolo.eu/blog/article109/google-ipv6-smtp-restrictions"&gt;It’s not just me that thinks this sucks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, we have an additional requirement:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Our MTA must be able to deliver only over IPv4, despite IPv6 being set up and configured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I set about testing a whole bunch of MTAs and I failed at gtting almost all of them working (mostly because of this IPv6 issue). Possibly this is because of lack of experience for Linux but whatever the reason, I needed something I could make work! Eventually I got PostFix working (or at least, I thought I did… keep reading!).&lt;/p&gt;

&lt;p&gt;The first thing I do when setting up a new &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt; is ensure everything is up-to-date. If your Raspbian image is quite old there might be many updates for you to install. This might take a little while:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="c"&gt;# Update the package list, update all packages and remove any packages that are no longer required&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we need to install and configure Postfix. When you install this it will automatically open a config utility (I don’t think it should, because of &lt;code class="highlighter-rouge"&gt;-y&lt;/code&gt; but it does…). &lt;strong&gt;Just select &lt;code class="highlighter-rouge"&gt;No Config&lt;/code&gt; and &lt;code class="highlighter-rouge"&gt;OK&lt;/code&gt;&lt;/strong&gt; since we’re going to write the entire config file ourselves.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get install postfix &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now to write the Postfix config file. We need to set a couple of things; descriptions are inline in comments:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tee /etc/postfix/main.cf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# Where to read account aliases, used to map all emails onto one account&lt;br data-jekyll-commonmark-ghpages="" /&gt;# and then on to a real email address&lt;br data-jekyll-commonmark-ghpages="" /&gt;alias_maps = hash:/etc/aliases&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# This sets the hostname, which will be used for outgoing email&lt;br data-jekyll-commonmark-ghpages="" /&gt;myhostname = pi.dantup.com&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# This is the mailserver to connect to deliver email&lt;br data-jekyll-commonmark-ghpages="" /&gt;# NOTE: This must be the MX server for the account you wish to deliver email to&lt;br data-jekyll-commonmark-ghpages="" /&gt;# or an open relay (but you hopefully won't find one of them). In my case, this&lt;br data-jekyll-commonmark-ghpages="" /&gt;# is Google's first MX server (which can be found by doing an MX lookup on my domain).&lt;br data-jekyll-commonmark-ghpages="" /&gt;relayhost = aspmx.l.google.com&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# Which interfaces to listen on. We don't want anyone connected to our Pi to send email,&lt;br data-jekyll-commonmark-ghpages="" /&gt;# so we set this to the local loopback interface only.&lt;br data-jekyll-commonmark-ghpages="" /&gt;inet_interfaces = loopback-only&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# This one is important for the reasons mentioned above. This means only IPv4 will be used,&lt;br data-jekyll-commonmark-ghpages="" /&gt;# avoiding the IPv6 restrictions Google have in place.&lt;br data-jekyll-commonmark-ghpages="" /&gt;inet_protocols = ipv4&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we need to create the aliases file referenced above. We simply map &lt;code class="highlighter-rouge"&gt;root&lt;/code&gt; mail on to our main user (&lt;code class="highlighter-rouge"&gt;danny&lt;/code&gt;) and then that user on to the required email address.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="c"&gt;# Set the aliases&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"root: danny"&lt;/span&gt; | &lt;span class="nb"&gt;sudo &lt;/span&gt;tee &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/aliases&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"danny: danny+pi@notreallymydomain.com"&lt;/span&gt; | &lt;span class="nb"&gt;sudo &lt;/span&gt;tee &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/aliases&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c"&gt;# Rebuild alias db and restart Postfix&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;newaliases&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; /etc/init.d/postfix start&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And that’s that. Simples! Next we need to send a test email to ensure everything is working. We send this to a local account (not a full email address) to ensure we’re testing the whole thing.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"FROM: root&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;TO: root&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Subject: Test email from the Raspberry Pi&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;This is a test email sent from your Raspberry Pi"&lt;/span&gt; | sendmail &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[33mTest email sent. Make sure it turns up :)&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[0m"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you’ve done everything right and Google (or your mailhost) don’t suspect you of being a spammer (which is possible if you’re connecting from a domestic IP address, but so far I’ve had no issues) you should have an email in your mailbox. For extra profit, add the sender to your address book and add a nice profile picture :)&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/pi_email.png" alt="Email from Raspberry Pi" /&gt;&lt;/p&gt;

&lt;p&gt;For extra points (and to reduce the chance of being labelled a spammer), you probably want to set up SPF records for your home IP address. If this changes a lot, you could use a dynamic DNS service. For example, my SPF record looks like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;spf1 include:_spf.google.com a:home.dantup.com ~all&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will allow Google and the IP that home.dantup.com resolves to to send mail and it be considered an SPF pass.&lt;/p&gt;

&lt;h2 id="not-so-fast"&gt;Not so fast…&lt;/h2&gt;

&lt;p&gt;At this point I thought everything was dandy… Until I rebooted! If you’re using Raspbian Jessie (but not Raspbian Jessie Lite) you’ll find that after a reboot that your email is broken. This reason for this is that Raspbian Jessie defaults to a &lt;code class="highlighter-rouge"&gt;Fast Boot mode&lt;/code&gt; where it does not wait for the network during booting. Postfixes creates a &lt;code class="highlighter-rouge"&gt;chroot jail&lt;/code&gt; and copies &lt;code class="highlighter-rouge"&gt;/etc/resolv.conf&lt;/code&gt; into it at startup, but in fast boot mode this is too early and it ends up blank. This means Postfix is unable to resolve DNS and no emails get sent!&lt;/p&gt;

&lt;p&gt;It took me several days to track down what was happening here but the fix is as simple as running &lt;code class="highlighter-rouge"&gt;sudo raspi-config&lt;/code&gt; and selecting &lt;code class="highlighter-rouge"&gt;Slow Boot&lt;/code&gt;, or running the following command:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;raspi-config nonint do_wait_for_network Slow&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will create a script that runs at startup which blocks until DHCP completes before allowing other services (like Postfix) to start up. I &lt;a href="https://github.com/RPi-Distro/repo/issues/24"&gt;posted my frustration with this default on GitHub&lt;/a&gt; and was informed that neither way was great, because &lt;code class="highlighter-rouge"&gt;Slow Boot&lt;/code&gt; would wait for a DHCP timeout (which could be quite long) if a Pi booted and wasn’t connected to the network.&lt;/p&gt;

&lt;p&gt;It’s worth noting that in Raspbian Jessie Lite the defaut is the &lt;code class="highlighter-rouge"&gt;Slow Boot&lt;/code&gt; (yay!) and therefore you don’t have to worry about this step.&lt;/p&gt;

&lt;p&gt;Hope this is helpful. If you have any problems, leave a comment. Bear in mind I’m a Linux noob and what’s written above might not be the best way to achieve this and I take no responsibility if anything breaks :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/04/setting-up-raspberry-pi-raspbian-jessie-to-send-email/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/WmoSLTavdqc" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/WmoSLTavdqc/" title="Setting up a Raspberry Pi (Raspian Jessie) to send Email" />
	<feedburner:origLink>https://blog.dantup.com/2016/04/setting-up-raspberry-pi-raspbian-jessie-to-send-email/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2016-03-04:/2016/03/installing-lighttpd-php7-and-letsencrypt-on-raspberry-pi-raspbian-jessie-lite/</id>
		<published>2016-03-04T00:00:00+00:00</published>
		<updated>2016-03-04T00:00:00+00:00</updated>
		
			<category term="Raspberry Pi" />
		
			<category term="Linux" />
		
		<title type="text">Installing Lighttpd, PHP 7 and LetsEncrypt on a Raspberry Pi (Raspbian Jessie Lite)</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I have a friend who wanted to serve some simple PHP scripts over HTTPS from a &lt;a href="https://amzredir.com/?asinus=B01C6Q2GSY&amp;amp;asinuk=B01CI5879A&amp;amp;tag=dtinfo"&gt;Raspberry Pi&lt;/a&gt;. He’d seen some benchmarks showing that Apache was a bit of a hog so was trying to use Nginx but was having trouble because none of these apps are in the Raspbian repos.&lt;/p&gt;

&lt;p&gt;I’m a total noob when it comes to Linux (I got my first Pi a week or so ago), but it didn’t seem like this should be a complicated setup, so I decided to see if I could quickly get it working. I have plenty of SD cards lying around and my Pi wasn’t doing anything important so there’s nothing to lose.&lt;/p&gt;

&lt;h2 id="goals"&gt;Goals&lt;/h2&gt;

&lt;p&gt;The main goal for me was to take a new install of Raspbian (I’m using Raspbian Jessie Lite, but standard Raspbian via NOOBS should be fine too) and render a PHP “Hello World” over HTTPS with a real cert trusted by browsers with minimal effort, while:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Preferring NGINX or Lighttpd to Apache&lt;/li&gt;
  &lt;li&gt;Preferring PHP 7 to anything older&lt;/li&gt;
  &lt;li&gt;Getting free TLS certs from &lt;a href="https://letsencrypt.org/"&gt;LetsEncrypt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Scheduling renewal of TLS certs via cron&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="replacements"&gt;Replacements&lt;/h2&gt;

&lt;p&gt;If you’re going to use any of these scripts, there are two things littered throughout that you must replace with your own values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="highlighter-rouge"&gt;/var/www/html&lt;/code&gt; is the webroot. I’m using the standard location Lighttpd (and Apache!) use out-of-the-box.&lt;/li&gt;
  &lt;li&gt;&lt;code class="highlighter-rouge"&gt;pi.dantup.com&lt;/code&gt; is the public hostname for the site/cert. This is mine; replace it with your own!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="complications"&gt;Complications&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The LetsEncrypt client is not available in the Raspbian Jessie repositories (nor debian Jessie)&lt;/li&gt;
  &lt;li&gt;PHP 7 is not available in the Raspbian Jessie repositories (nor Debian Jessie or Jessie Backports)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="getting-raspbian"&gt;Getting Raspbian&lt;/h2&gt;

&lt;p&gt;Step 1 is to get a working Raspbian install. I chose to use &lt;a href="https://www.raspberrypi.org/downloads/raspbian/"&gt;Raspbian Jessie Lite from here&lt;/a&gt; because I tend to run my Pis headless. These instructions should all be the same for standard Raspbian (inc. via NOOBS).&lt;/p&gt;

&lt;h2 id="security-considerations"&gt;Security Considerations&lt;/h2&gt;

&lt;p&gt;If you’re going to expose your Pi (or any other computer) to the web, you should consider the security. Someone breaching a device on your network could give them access to other devices inside your network! Some things you might wish to consider doing to your Pi if you’re going to expose it (especially if opening SSH):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create your own user and ditch the &lt;code class="highlighter-rouge"&gt;Pi&lt;/code&gt; user (at a minimum, change the password to something strong!)&lt;/li&gt;
  &lt;li&gt;Disable root login over SSH&lt;/li&gt;
  &lt;li&gt;Change the SSH port&lt;/li&gt;
  &lt;li&gt;Set up &lt;a href="https://wiki.debian.org/UnattendedUpgrades"&gt;unattended security upgrades&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve been working on a script that does this (and more) for me when I set up a new Pi. I’m hoping to finish/tidy/blog it over the coming weeks.&lt;/p&gt;

&lt;h2 id="setting-up-raspbian"&gt;Setting up Raspbian&lt;/h2&gt;

&lt;p&gt;First thing I do when setting up Raspbian is expand the filesystem (you won’t need to do this if using NOOBS, but will if you’ve written a Raspbian &lt;code class="highlighter-rouge"&gt;.img&lt;/code&gt;) and ensure all packages are up-to-date.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="c"&gt;# Expand the filesystem and reboot&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;raspi-config &lt;span class="nt"&gt;--expand-rootfs&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c"&gt;# Update lists, perform upgrades, remove orphaned packages&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="switch-to-root-shell"&gt;Switch to root shell&lt;/h2&gt;

&lt;p&gt;Pretty much everything here requires root, so it’s easiest to switch to a root shell to avoid prefixing everything with &lt;code class="highlighter-rouge"&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="installing-lighttpd"&gt;Installing Lighttpd&lt;/h2&gt;

&lt;p&gt;This one is pretty painless. Install via &lt;code class="highlighter-rouge"&gt;apt&lt;/code&gt; and replace the default page with our own Hello World.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;apt install lighttpd &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;rm /var/www/html/index.lighttpd.html&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/www/html/index.html&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At this point you should be able to visit http://raspberrypi/ (assuming you haven’t changed the hostname) and see your Hello World message. It’s a good start, but it’s not HTTPS!&lt;/p&gt;

&lt;h2 id="installing-packages-from-debian"&gt;Installing packages from Debian&lt;/h2&gt;

&lt;p&gt;Because neither PHP 7 nor the LetsEncrypt client are in the Raspbian repos, we need to fetch them from the Debian ones. Before &lt;code class="highlighter-rouge"&gt;apt&lt;/code&gt; will download things from there, we need to trust them. You should probably confirm these keys (such as by skipping this step, letting the next step fail, and then getting the keys from the error message) rather than copy/paste them from some random guys blog (which wasn’t served over HTTPS) though!&lt;/p&gt;

&lt;p&gt;The error you’ll get prior to trusting these keys looks like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;W: GPG error: http://http.debian.net jessie-backports InRelease: The following&lt;br data-jekyll-commonmark-ghpages="" /&gt;signatures couldn&lt;span class="s1"&gt;'t be verified because the public key is not available:&lt;br data-jekyll-commonmark-ghpages="" /&gt;    NO_PUBKEY 8B48AD6246925553 NO_PUBKEY 7638D0442B90D010&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I don’t know why there are two. Did I mention I’m a Linux noob?&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; pgpkeys.mit.edu &lt;span class="nt"&gt;--recv-key&lt;/span&gt;  8B48AD6246925553      &lt;br data-jekyll-commonmark-ghpages="" /&gt;gpg &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--export&lt;/span&gt; 8B48AD6246925553 | apt-key add -&lt;br data-jekyll-commonmark-ghpages="" /&gt;gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; pgpkeys.mit.edu &lt;span class="nt"&gt;--recv-key&lt;/span&gt; 7638D0442B90D010      &lt;br data-jekyll-commonmark-ghpages="" /&gt;gpg &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--export&lt;/span&gt; 7638D0442B90D010 | apt-key add -&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="install-the-letsencrypt-client"&gt;Install the LetsEncrypt Client&lt;/h2&gt;

&lt;p&gt;The LetsEncrypt client is available from Jessie Backports. We’ll need to add the source to be able to install it from there. My script removes the source at the end (and re-updates the packages; I don’t know if that’s required) since we only want to borrow these packages and not use this for anything more.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb http://httpredir.debian.org/debian jessie-backports main contrib non-free"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/debian-jessie-backports.list&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt install letsencrypt &lt;span class="nt"&gt;-t&lt;/span&gt; jessie-backports &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;rm /etc/apt/sources.list.d/debian-jessie-backports.list&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="requesting-tls-certs"&gt;Requesting TLS Certs&lt;/h2&gt;

&lt;p&gt;In order for the LetsEncrypt client to work, it needs to be able to connect to your Pi using the hostname you want the cert for (this is to verify you actually own this domain, or at least, control the web server it points at). This may mean setting up DNS records to point the domain at your Pi and/or forwarding ports from a router (you’ll need port 80 for this step, but may as well also map 443 while you’re at it to save coming back to it later).&lt;/p&gt;

&lt;p&gt;Because there’s no automatic module to set up &lt;code class="highlighter-rouge"&gt;Lighttpd&lt;/code&gt; we need to use the &lt;code class="highlighter-rouge"&gt;--webroot&lt;/code&gt; method (this means the client will write files into your webroot and LetsEncrypt will connect back and read them). Pass each webroot with &lt;code class="highlighter-rouge"&gt;-w&lt;/code&gt; and the domain with &lt;code class="highlighter-rouge"&gt;-d&lt;/code&gt;. You can add multiple sets of these as required.&lt;/p&gt;

&lt;p&gt;Remember to replace the webroot and domain with your own!&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;letsencrypt certonly &lt;span class="nt"&gt;--webroot&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /var/www/html &lt;span class="nt"&gt;-d&lt;/span&gt; pi.dantup.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="setting-up-tls-in-lighttpd"&gt;Setting up TLS in Lighttpd&lt;/h2&gt;

&lt;p&gt;Lighttpd expects certs to be combined, so we need to concatonate them before we can configure it. Remember to replace your domain in the path (note: this is in /etc/letsencrypt and not your webroot!).&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; /etc/letsencrypt/live/pi.dantup.com/&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;privkey.pem cert.pem &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; combined.pem&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;popd&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we need to add TLS config for Lighttpd. We need to point at the cert we just combined as well as the full chain certificate that will be served up to the client browser. We also disable SSLv2 and SSLv3 for security.&lt;/p&gt;

&lt;p&gt;Again, be sure to replace your domain in the certificate paths.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;tee /etc/lighttpd/conf-enabled/letsencrypt.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="nv"&gt;$SERVER&lt;/span&gt;&lt;span class="sh"&gt;["socket"] == ":443" {&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.engine = "enable"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.pemfile = "/etc/letsencrypt/live/pi.dantup.com/combined.pem"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.ca-file =  "/etc/letsencrypt/live/pi.dantup.com/fullchain.pem"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:HIGH:!MD5:!aNULL:!EDH:!AESGCM"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.honor-cipher-order = "enable"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.use-sslv2 = "disable"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        ssl.use-sslv3 = "disable"&lt;br data-jekyll-commonmark-ghpages="" /&gt;}&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;/etc/init.d/lighttpd force-reload&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At this point, you should now be able to visit https://raspberrypi/ (assuming you haven’t changed the hostname) and see your Hello World message again. You’ll get a warning about the cert name not matching (since you’re not accessing it via the real domain), but that’s expected. Accessing it via the real domain may work depending on your setup. Hurrah! Encryption!&lt;/p&gt;

&lt;h2 id="installing-php-7"&gt;Installing PHP 7&lt;/h2&gt;

&lt;p&gt;Like the LetsEncrypt client, PHP 7 is not available in the Raspbian repos. Nor is it available in Debian Jessie Backports. However it is available in Stretch (the next version after Jessie),so we can do a similar thing and get it from there.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb http://httpredir.debian.org/debian stretch main contrib non-free"&lt;/span&gt; | tee /etc/apt/sources.list.d/debian-stretch.list&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt install php7.0 php7.0-fpm &lt;span class="nt"&gt;-t&lt;/span&gt; stretch &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;rm /etc/apt/sources.list.d/debian-stretch.list&lt;br data-jekyll-commonmark-ghpages="" /&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we need to enable &lt;code class="highlighter-rouge"&gt;fastcgi&lt;/code&gt; and tell &lt;code class="highlighter-rouge"&gt;Lighttpd&lt;/code&gt; where to find PHP.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;tee /etc/lighttpd/conf-enabled/php.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;fastcgi.server += (".php" =&amp;gt; ((&lt;br data-jekyll-commonmark-ghpages="" /&gt;        "socket" =&amp;gt; "/var/run/php/php7.0-fpm.sock"&lt;br data-jekyll-commonmark-ghpages="" /&gt;)))&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;lighttpd-enable-mod fastcgi&lt;br data-jekyll-commonmark-ghpages="" /&gt;/etc/init.d/lighttpd force-reload&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, let’s replace our boring old Hello World with a nice PHP one!&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;rm /var/www/html/index.html&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;?php echo "&amp;lt;h1&amp;gt;Hello, World (from PHP)!&amp;lt;/h1&amp;gt;"; ?&amp;gt; '&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/www/html/index.php&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you’ve done all this right, https://raspberrypi/ (assuming you haven’t changed the hostname) will now greet you from PHP! Almost done…&lt;/p&gt;

&lt;h2 id="automatic-tls-cert-renewal"&gt;Automatic TLS Cert Renewal&lt;/h2&gt;

&lt;p&gt;Not renewing your certificates would be pretty embarassing, so we should make that automatic. We’ll do this via Cron. Cron sends emails with the output of commands, so if you’ve set up emails to be forwarded to a real mailbox (this will be covered in my Pi setup script which I hope to blog in the coming weeks) you’ll get a note each time this runs. We’ll set it to run weekly since by default it only renews certs that’ll expire in the next 30 days, so monthly could cause them to be missed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Mike Scalora &lt;a href="#comment-2671739539"&gt;posted&lt;/a&gt; a &lt;a href="https://gist.github.com/mscalora/94f384d1311f66ac09ea6d31d77a102e"&gt;better version&lt;/a&gt; of this that handles multiple domains neatly in the comments :)&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-bash" data-lang="bash"&gt;tee /etc/cron.weekly/letsencrypt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# Renew cert&lt;br data-jekyll-commonmark-ghpages="" /&gt;letsencrypt renew&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# Rebuild the cert&lt;br data-jekyll-commonmark-ghpages="" /&gt;pushd /etc/letsencrypt/live/pi.dantup.com/&lt;br data-jekyll-commonmark-ghpages="" /&gt;cat privkey.pem cert.pem &amp;gt; combined.pem&lt;br data-jekyll-commonmark-ghpages="" /&gt;popd&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;# Reload&lt;br data-jekyll-commonmark-ghpages="" /&gt;/etc/init.d/lighttpd force-reload&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;chmod +x /etc/cron.weekly/letsencrypt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="thats-it"&gt;That’s It!&lt;/h2&gt;

&lt;p&gt;I believe that’s everything! If you hit problems please post in the comments and I’ll try to fix them up. Please note that I’m a Linux noob, some (or all) of this might not be done in the best possible way. Again, I’ll make tweaks if people send corrections/improvements :-)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2016/03/installing-lighttpd-php7-and-letsencrypt-on-raspberry-pi-raspbian-jessie-lite/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/NsX4Qi9U3bQ" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/NsX4Qi9U3bQ/" title="Installing Lighttpd, PHP 7 and LetsEncrypt on a Raspberry Pi (Raspbian Jessie Lite)" />
	<feedburner:origLink>https://blog.dantup.com/2016/03/installing-lighttpd-php7-and-letsencrypt-on-raspberry-pi-raspbian-jessie-lite/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2015-12-15:/2015/12/my-week-in-a-tesla-model-s-p85/</id>
		<published>2015-12-15T00:00:00+00:00</published>
		<updated>2015-12-15T00:00:00+00:00</updated>
		
			<category term="EV" />
		
		<title type="text">My Week in a Tesla Model S P85</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;9 months ago my wife and I took delivery of two Renault ZOEs. We hadn’t had EVs before but after test driving a ZOE and being offered a great deal on two we found it hard to turn down! Although the range of the ZOE is only 70-100 miles (depending on weather, etc.) we’ve had no problems using the ZOE for a summer holiday racking up over a thousand miles in a week, including two 270 mile trips to/from the holiday park. Neither of us would willingly go back to combustion cars.&lt;/p&gt;

&lt;p&gt;In August, Chargemaster Plc (who made the chargepoint we have at home and also run the POLAR charging network) &lt;a href="https://www.chargemasterplc.com/index.php/blog/polar-plus/"&gt;launched a new scheme called “POLAR Plus”&lt;/a&gt; which rewarded customers for charging on their network with points &lt;a href="http://polar-network.com/experience"&gt;which could be used to “bid”&lt;/a&gt; for a weeks use of one of their fleet of electric vehicles (including a Tesla Model S and a BMW i8!). The number of points needed to loan the Tesla Model S was only 70 (which is just 7 qualifying charge sessions). I was sceptical but &lt;a href="https://twitter.com/ChargemasterPlc/status/631766409538736128"&gt;they assured me&lt;/a&gt; all was as it seemed. The scheme costs £7.85 per month but the first six months are free.&lt;/p&gt;

&lt;p&gt;Fast forward to November and it happened, my week in Chargemaster’s Tesla Model S P85 was confirmed, 8th - 14th December! At this time I’m still in my free 6-month trial so I’ve paid £0 to Chargemaster! :)&lt;/p&gt;

&lt;div class="filmstrip"&gt;
	&lt;img src="/post_images/tesla/me_1_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/me_2_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/gadget_3_t.jpg" /&gt;
&lt;/div&gt;

&lt;p&gt;I picked up the car from Chargemaster’s head office in Luton on Tuesday 8th Dec. At more than twice the price of the most expensive car I’ve owned I was expecting it to be great but it still seriously exceeded my expectations. I wanted one from the minute I sat inside it! Evie (an apt name) from Chargemaster ran me through some paperwork and the basics of the Model S and we were soon on our way home. It should be a 3-4hr journey back to where we live in Cheshire but with terrible traffic and a stop at Warrington to try out the Supercharger it took approximately 5hrs driving (mostly in the dark and rain) to get home.&lt;/p&gt;

&lt;h2 id="exterior"&gt;Exterior&lt;/h2&gt;

&lt;p&gt;I’d already seen pictures/videos of the Model S but it looked much better than I expected up-close. If you’re used to spending this sort of money on a car you may be used to having cars that look this nice but sadly, I am not! It looked stunning and it was pretty huge (especially compared to the ZOE which is quite tiny).&lt;/p&gt;

&lt;div class="filmstrip"&gt;
	&lt;img src="/post_images/tesla/ext_1_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/ext_2_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/ext_3_t.jpg" /&gt;
&lt;/div&gt;

&lt;h2 id="performance"&gt;Performance&lt;/h2&gt;

&lt;p&gt;This Model S was a P85. This is the rear-wheel-drive “performance” version (the all-wheel-drive performance version is the P85D). It’s not available in this configuration anymore but I believe the 0-60 time is something like 4.1 seconds. To me, this qualifies as “stupidly fast”. For various reasons (public roads, very wet week, it’s not my car and being somewhat scared of the performance I did experience) I didn’t get to experience how fast this thing would accelerate foot-to-the-floor but it was quite clear from just a little bit of “fun” I had that the power in this car is utterly ridiculous. I’ve never driven a RWD car before and feeling the back end twitching in the wet is scary. If I somehow ended up with the money to buy one of these I think I’d opt for the “D” version just to avoid that danger! :D&lt;/p&gt;

&lt;h2 id="interior"&gt;Interior&lt;/h2&gt;

&lt;p&gt;Inside, the Model S feels expensive. With a few exceptions (the plastic on the front of the arm rest rattled a little and felt a bit cheap) everything feels very premium and well-made. There’s nice stitched leather around the dash and the leather seats are very comfortable. As there’s no handbrake/gear-selector between the front seats, everything is very open and spacious. The back easily accommodates adults with decent legroom and was big enough that the kids couldn’t reach to kick the back of the seat. Bonus!&lt;/p&gt;

&lt;div class="filmstrip"&gt;
	&lt;img src="/post_images/tesla/int_1_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/int_2_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/int_3_t.jpg" /&gt;
&lt;/div&gt;

&lt;h2 id="softwaretouchscreen"&gt;Software/Touchscreen&lt;/h2&gt;

&lt;p&gt;The thing that is always a huge disappointment in cars I’ve owned is the software. Every time I get a new car I think “software must’ve moved on, it’ll be good now”, but no. The Model S bucks this trend and has excellent software on an enormous 17” touch screen front-centre. Rather than hardware dials/knobs that can’t be changed (and can break) the Model S does almost everything in software from this same touchscreen. My wife initially said the touchscreen was “too big” but I think she was coming around to it by the end of the week. It has Google Maps-based navigation, the reversing camera, calendar, Bluetooth/music/radio, consumption graphs and everything else you can think of all together in one place. All the settings you might like to tweak (regen, creep, suspension/ride height, sunroof, charge timer, automatic lights etc.) are all controlled here. Whereas many cars feel like a Frankenstein of disparate systems “integrated” together, everything here feels like it was built as one.&lt;/p&gt;

&lt;p&gt;The excellent software extends to the display behind the steering wheel which shows a small version of the navigation map (when navigating) so you can keep your eyes directly ahead.&lt;/p&gt;

&lt;h2 id="bootfrunk-storage"&gt;Boot/Frunk Storage&lt;/h2&gt;

&lt;p&gt;Although it’s not very deep, the boot is rather large and easily fit our pushchair, coats and plenty of shopping. There’s also a really decent amount of storage under the bonnet (“frunk”/”front trunk”) that could easily fit all of our shopping in it and would be less likely to roll around than in the boot.&lt;/p&gt;

&lt;div class="filmstrip"&gt;
	&lt;img src="/post_images/tesla/storage_1_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/storage_2_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/storage_3_t.jpg" /&gt;
&lt;/div&gt;

&lt;h2 id="regen-braking"&gt;Regen Braking&lt;/h2&gt;

&lt;p&gt;When I picked the car up the regen braking was set to “Low”. I switched it to “Standard” as I was quite used to regen braking in the ZOE and liked minimising the use of the brake pedal. I was surprised at just how aggressive “Standard” was and found myself having to put my foot back on the gas after lifting off many times while I got used to it. Once I got the hang of it, it was absolutely brilliant - I previously thought I didn’t use the brake pedal much in the ZOE but this was just miles better. One-pedal-driving is great!&lt;/p&gt;

&lt;p&gt;On one of the days it was around 5 degrees Celsius. I noticed the regen braking wasn’t anywhere near as effective (it caught me by surprise) and I noticed the speedo showed a dotted line in the regen area which indicates that it can’t regen/charge at the normal rate. It was a little weird that the cold weather was changing the driving experience so much but after just a little driving/braking it seemed to have warmed up enough for this to go away and things were back to normal.&lt;/p&gt;

&lt;h2 id="gadgets"&gt;Gadgets&lt;/h2&gt;

&lt;p&gt;The Model S comes with a bunch of nice things/gadgets, many of which aren’t unique to the Model S (or even EVs) but they still added to the experience.&lt;/p&gt;

&lt;p&gt;With the exception of the rear view mirror all adjustments are electric (seats, wing mirrors and even steering wheel adjustment) and saved against your driver profile. If someone else drives the car, selecting your profile afterwards will move everything back to how you had it.&lt;/p&gt;

&lt;p&gt;The wing mirrors automatically angle downwards when you shift into reverse to help you see white lines or parking spaces on the road (and return when you shift out of reverse).&lt;/p&gt;

&lt;p&gt;The reversing camera is far higher definition than the car in our ZOE and shows on the huge screen making it really easy to reverse-park (which is super useful because forwards parking is tricky when you don’t really know how big the car is and are unable to see the front corners!).&lt;/p&gt;

&lt;p&gt;Front seats are heated. I’ve never cared much for these in the past but with the cold weather they were &lt;em&gt;really&lt;/em&gt; nice! Something small but I liked, was that when you turned them on they defaulted to the highest setting and reduced as you continue to tap the button. This matches how you’d use them pretty well (it’s cold, put them on full then reduce over time) so you don’t have to keep tapping to increase the heat when you turn them on. Unfortunately there was no heated steering wheel to go with it and the wheel was often quite cold so I found myself driving around all toasty and warm but with cold hands!&lt;/p&gt;

&lt;p&gt;The key for the car is a miniature Model S. Pressing the front opens the frunk, the back opens the boot and the middle opens the car. It’s a nice touch and looks pretty cool.&lt;/p&gt;

&lt;p&gt;When I was returning the car I entered the business park and the car showed a message along the lines of “Increasing ride height based on location”. I don’t know how this had been set up, but due to speed bumps the ride height was increased!&lt;/p&gt;

&lt;div class="filmstrip"&gt;
	&lt;img src="/post_images/tesla/gadget_1_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/gadget_2_t.jpg" /&gt;
	&lt;img src="/post_images/tesla/gadget_3_t.jpg" /&gt;
&lt;/div&gt;

&lt;h2 id="negatives"&gt;Negatives&lt;/h2&gt;

&lt;p&gt;There really aren’t many things we didn’t like about the Model S, it really is an excellent car. There are a couple of niggles or things I think could be improved though:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Chargeflap seemed to stick a lot; took lots of locking/unlocking before it would open (even the button on the screen didn’t seem to work). On the way back to Luton I had to give up trying to top it up at the services because of this (though unsurprisingly, it appeared to be fine when we arrived at Chargemaster HQ!).&lt;/li&gt;
  &lt;li&gt;The plastic at the front of the arm rest rattled a little which made it feel cheap. I don’t know if they all do this or it was just this car.&lt;/li&gt;
  &lt;li&gt;Can’t pre-heat the car from the fob (the ZOE can do this), so if you’re going out unexpectedly you might have a cold car. It’s possible the app allows you to do this but sadly we didn’t have access to that (it requires a username/password we didn’t have).&lt;/li&gt;
  &lt;li&gt;Can’t set a pre-heat timer within the car (again, ZOE does this), only enable “smart preconditioning” (which doesn’t help if your schedule isn’t fixed or you’re loaning a car that has been used by others). Again, the app might allow this but I was unable to check.&lt;/li&gt;
  &lt;li&gt;At one point I was driving along and the sunroof dumped a load of water on my passenger! I don’t think the sunroof was leaking and it hadn’t been opened in days but I suspect water had collected somewhere inside (it had been opened when it was a little wet a few days earlier) and a hill/bend caused it to come dripping out.&lt;/li&gt;
  &lt;li&gt;Because the cold weather affects the regen braking, it’d be nice if there was an option for the car to make up for it with friction braking so that the braking/one-pedal-driving experience doesn’t vary so much as the weather changes or battery warms up. It’s a surprise to lift your foot off the accelerator and the car not slow down as much as it normally does.&lt;/li&gt;
  &lt;li&gt;The price… Before I picked the car up I thought it was overpriced. I don’t think this anymore! However even if it’s worth the money it’s still way out of my price range so of course I wish it was cheaper! =D&lt;/li&gt;
  &lt;li&gt;I was a little paranoid about the key not being directly attached to my keyring but just in a pouch which it looked like it could easily have slipped out. It didn’t come out for me, but I wouldn’t be surprised if people have lost their “keys” because of this.&lt;/li&gt;
  &lt;li&gt;The Satnav doesn’t show the speed limit for the current road. I’m used to having this in TomTom in the ZOE and kept looking for it. I know the Autopilot version shows this (read from the road signs; maybe only in US?) but there’s no reason it can’t use a database too (presumably it has one as part of the Google mapping for travel times?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="other-opinions"&gt;Other Opinions&lt;/h2&gt;

&lt;p&gt;Everybody that I took out in the Model S seemed to love it. Even the people that previously weren’t entirely sold on current-gen EVs (for various reasons) like my parents loved it. My mum (who doesn’t really care about cars) text me saying “it’s ace” and I think if my dad could possibly make the numbers work out he would place his order today. We definitely convinced a bunch more people this week that EVs can be great and that it’s undoubtedly not a fad that’s going to disappear.&lt;/p&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;It was a great experience driving the Model S for a week and I’m really grateful to Chargemaster for making it happen. It’s expected that the Model 3 may be released before we plan to return our ZOEs at the end of their PCP term and may only cost £30,000 so I’ll be keeping a close eye on that and what government incentives we have at the time. Chargemaster told me they’ve ordered their Model X and it will be added to the EV Experience scheme when it arrives (expected late Q2/early Q3 2016).&lt;/p&gt;

&lt;p&gt;If I ever find myself with £80,000 to spend on a new car, I know what will be top of the shopping list. In the meantime, I guess I’m stuck with the ZOE (except for if/when I’m able to borrow more cars from Chargemaster!).&lt;/p&gt;

&lt;h2 id="going-back-to-the-zoe"&gt;Going Back to the ZOE&lt;/h2&gt;

&lt;p&gt;Today I got to do my commute back in the ZOE. It was an interesting change! The ZOE seating position is much higher (despite the Model S also having batteries along the bottom) and the car feels like it has a much higher centre of gravity. The reversing camera is a tiny low-definition mess. The weak regen braking caught me out a few times and I found myself having to hit the brakes while I got used to it. Although the ZOE felt fast compared to my previous petrol car (a 1.6 petrol Megane) it feels &lt;em&gt;really&lt;/em&gt; slow compared the Model S and the accelerator is (unsurprisingly) just nowhere near as responsive.&lt;/p&gt;

&lt;p&gt;All that said, once I got used to driving the ZOE again towards the end of my commute, I was reminded how fun it was. Driving the really Model S hasn’t changed that and I still arrived at work with a big grin on my face!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2015/12/my-week-in-a-tesla-model-s-p85/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/9bpgPgir-y4" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/9bpgPgir-y4/" title="My Week in a Tesla Model S P85" />
	<feedburner:origLink>https://blog.dantup.com/2015/12/my-week-in-a-tesla-model-s-p85/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2015-09-20:/2015/09/simple-windows-browser-selector/</id>
		<published>2015-09-20T00:00:00+00:00</published>
		<updated>2015-09-20T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term=".NET" />
		
			<category term="Windows" />
		
		<title type="text">Simple Windows utility to act as default browser and launch different browsers based on domain</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/BrowserSelector"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently saw &lt;a href="https://twitter.com/ckindel/status/644970792095080448"&gt;a tweet&lt;/a&gt; about having different default browsers on Windows for different urls. This seemed like an interesting idea - I use a few systems that work better in one browser than another. I retweeted and saw a few responses from others that seemed interested in the idea.&lt;/p&gt;

&lt;p&gt;It seemed like a pretty easy thing to do - register as a browser and when a url comes in, just bounce it over to the correct “real” browser based on some config. So I thought I’d knock something together…&lt;/p&gt;

&lt;p&gt;It didn’t take long, nor much code. There are about 30 lines of code to create (or delete) registry values to register with Windows as a browser, about 60 lines of code to parse a poor version of an INI file, and around 50 lines of plumbing to pull them together and look up the URLs.&lt;/p&gt;

&lt;p&gt;Code is up on &lt;a href="https://github.com/DanTup/BrowserSelector"&gt;GitHub (DanTup/BrowserSelector)&lt;/a&gt;, and a &lt;a href="https://github.com/DanTup/BrowserSelector/releases"&gt;binary release&lt;/a&gt; if you just want to run it.&lt;/p&gt;

&lt;p&gt;Config looks like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="c"&gt;; Default browser is first in list&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="nn"&gt;[browsers]&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="py"&gt;chrome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s"&gt;rogram Files (x86)&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="s"&gt;oogle&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s"&gt;hrome&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="s"&gt;pplication&lt;/span&gt;&lt;span class="se"&gt;\c&lt;/span&gt;&lt;span class="s"&gt;hrome.exe&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="py"&gt;ie&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;iexplore.exe&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c"&gt;; Url preferences.&lt;br data-jekyll-commonmark-ghpages="" /&gt;; Only * is treated as a special character (wildcard).&lt;br data-jekyll-commonmark-ghpages="" /&gt;; Matches are domain-only. Protocols and paths are ignored.&lt;br data-jekyll-commonmark-ghpages="" /&gt;; Use "*.blah.com" for subdomains, not "*blah.com" as that would also match "abcblah.com".&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;/span&gt;&lt;span class="nn"&gt;[urls]&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="py"&gt;microsoft.com&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ie&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="py"&gt;.microsoft.com&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ie&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s a first version, so I’m sure it has bugs and can be improved. I can’t make any promises to evolve it further (it’s hard to find coding time since kids came along!) but since it’s pretty trivial maybe it won’t need much work to improve and fix bugs.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2015/09/simple-windows-browser-selector/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/lGaBPTXtfuE" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/lGaBPTXtfuE/" title="Simple Windows utility to act as default browser and launch different browsers based on domain" />
	<feedburner:origLink>https://blog.dantup.com/2015/09/simple-windows-browser-selector/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2015-07-27:/2015/07/response-from-cyc/</id>
		<published>2015-07-27T00:00:00+00:00</published>
		<updated>2015-07-27T00:00:00+00:00</updated>
		
			<category term="EV" />
		
		<title type="text">Response from CYC</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I &lt;a href="/2015/07/gmev-and-cyc-the-uks-awful-recharging-network/"&gt;recently blogged about poor experiences using the CYC network&lt;/a&gt; (mostly GMEV chargers) and sent the post to CYC. Today CYC responded.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hi Dan,&lt;/p&gt;

  &lt;p&gt;Thanks for your email. I’ll work down your blog post point by point if that’s ok?&lt;/p&gt;

  &lt;p&gt;First of all, the problems you’ve have had with the App. We’re a little hamstrung here as our App was built by a third party who still retain most of the administrational rights for it so all changes need to go through them and be made on their side of things. When you first reported the email problem this was immediately sent to them to correct. I will also make them aware of the App security issues you have flagged up but I assure you that both CYC and our developers are fully PCI Compliant.&lt;/p&gt;

  &lt;p&gt;The mistakenly taken £1 pre-auths – I use the word mistakenly because I think ‘fraudulently’ is quite harsh – thank you for flagging this is up us. As we told you at the time, this was a problem in the way the app had been set up with Paypoint and when someone registered their card an error in the code meant that Paypoint counted it as a transaction and not a pre-auth. As soon as you flagged this up we reported it to the developers and after a fair bit of troubleshooting the problem was identified and rectified. CYC then retroactively went through and refunded everyone who had ever signed up their £1 – Including me, so thank you.&lt;/p&gt;

  &lt;p&gt;You will be pleased, I’m sure, to know that we are currently building a new App in-house that will hopefully solve a large amount of these issues and improve communications between the app and post. We (or I at least) are happy to admit that the App isn’t great atm. It’s sticky, it lags, it’s clunky. Previous management were keen to be first to market with the app and so a lot of mistakes were made that will hopefully be corrected in the new version. The wireframes for the new app have recently been signed off and so we hope it will be ready by late September, however this is dependent on how many cups of coffee and cans of energy drink we can pour down Joe.&lt;/p&gt;

  &lt;p&gt;RFID Cards attached to wrong accounts – This has happened on the odd occasion unfortunately but has been rectified as soon as it’s been flagged up. CYC Applications are processed by a member of the team here and, as is always the way, human error can occur. As much as I’d like to delete my colleagues, I do enjoy their company on occasion and so I have asked them to be far more vigilant. As far as charging drivers for usage that isn’t their own, please rest assured that any charges levied in error will be removed and ANY money taken in error will ALWAYS be refunded in full as soon as it’s flagged up. You seem to be under the impression that we’re out to stiff drivers and take their money – I can categorically say that this is not the case. We’re actually quite nice people.&lt;/p&gt;

  &lt;p&gt;RFID Cards. You hate RFID cards, I hate RFID cards, everyone hates RFID cards. Unfortunately though for funding to be issued for a charger, it NEEDS to have a unique access method so its usage and EV uptake can be monitored. There is currently no way around it – Although we are looking into alternatives. As such, we offer RFID cards priced at £20 per annum. Transport for Greater Manchester do subsidise RFID Cards for drivers within their remit, this was done as a way of encouraging EV uptake in the region and the cards are priced at £10 ONE OFF. As you can imagine, a lot of people would rather pay £10 one off than £20 per annum and so we get drivers from outside of the region trying to register (Holland being the furthest so far) If we get an application from outside of the region but close to Manchester (such as yourself) we ask TfGM about the application. As we explained to you at the time, they confirmed that you fell outside of their area.&lt;/p&gt;

  &lt;p&gt;We did say at the time that you could apply for a CYC Access Card if you wanted and we explained to you why we could not give you one for free in the same way that other networks do. The income generated by Access Cards (actually very little when you take away the VAT, cost of the card, postage and admin time) is directly used to help fund the CYC Helpdesk and our Out of Hours support – Something which we DO NOT wish to withdraw. Sorry to jump to another point, but it seems to lead on nicely.&lt;/p&gt;

  &lt;p&gt;The problem you had this weekend with a trapped cable. You are correct, the main CYC office is closed at weekends. At present we operate 08.00-18.00 Mon-Fri. Outside of these hours we operate an emergency support number. This is a member of the CYC team with a mobile phone and laptop who, no matter what time of night or day, is available to aid stranded drivers. If you call the office number you will hear a message stating that the office is closed but if you have a trapped cable please call a number. Had you of called that number, the member of staff on call this weekend would have been more than happy to stop what they were doing and release your cable.&lt;/p&gt;

  &lt;p&gt;Just so we’re clear, the staff member on Out of Hours only has limited access to systems and is only there for releasing trapped cables. However we never knowingly leave a driver stranded. There have been many, many occasions where we’ve started sessions, reset posts, directed to nearby chargers, helped non-CYC members get a charge etc… The reason the phone does not simply direct to the team member is in an attempt divert what we’d deem ‘non-emergencies’ to business hours, for example, a call at 5am because a driver couldn’t remember their Apple ID.&lt;/p&gt;

  &lt;p&gt;Incorrect charger locations and blank numbers – This is hugely disappointing. When a CP is commissioned the installing engineer takes a lat/lon reading from the post and send it to us. If this is incorrect, we’re in trouble from the get go. As you can imagine we don’t know where each of our chargers are, we’re based in Brighton and I haven’t even set foot in Manchester for years, so we rely on correct information from others. The same goes for parking information. The CYC site showed what we were given.  As for the stickers, I am looking into why these haven’t been applied at the moment. I assume they were and some hilarious child removed them. Clearly we need to rethink how we number our Charge Points if they can be removed so easily.&lt;/p&gt;

  &lt;p&gt;Regarding the failed sessions at various Charge Points, if your starttransation message was reaching our servers, there would be a record of it. If it hasn’t reached us, we don’t know about it. By the sounds of it, it looks like you’ve had several occasions when, for whatever reason, the starttransation has not made it to our server. We’re looking into why this is but for the time being I have removed the chargers at Hulme Street, Bury Market and Media City from the App. Looking that the records from last week there were a number of successful charge sessions, started by the App, at the Trafford Centre and I know this is a popular location so these have not been removed at present. Again, we hope that the new version of the app will solve a lot of these issues but it’s worth mentioning that last week the CYC estate clocked up 762 Charging Sessions started with the App. 43 of which were in Manchester.&lt;/p&gt;

  &lt;p&gt;Lastly, I’d like to thank you for reporting these problems to us and I hope the fact that I’ve given such a thorough and frank reply will go some way to convincing you that we do actually care.&lt;/p&gt;

  &lt;p&gt;If you have any more questions or comments I would be more than pleased to help.&lt;/p&gt;

  &lt;p&gt;Many thanks&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So CYC are now certainly aware of the experience that I (and maybe others) have had. It’s great that CYC took the time to respond in this detail :)&lt;/p&gt;

&lt;p&gt;It’s interesting that they’re sure that if the “charge start” request hits their server, it will be logged. If the issue we’re seeing is not connectivity, then maybe it’s fixable in software (I assumed it would need better mobile antennas in the chargers). I’m hoping to proxy some failing “start charge” requests next time we go to Manchester (if I can find a way to do such a think on Android without a portable machine running Fiddler!) in the hope of maybe helping understand this unreliability.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2015/07/response-from-cyc/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/X84vhFR0Nek" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/X84vhFR0Nek/" title="Response from CYC" />
	<feedburner:origLink>https://blog.dantup.com/2015/07/response-from-cyc/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2015-07-25:/2015/07/gmev-and-cyc-the-uks-awful-recharging-network/</id>
		<published>2015-07-25T00:00:00+00:00</published>
		<updated>2015-07-25T00:00:00+00:00</updated>
		
			<category term="EV" />
		
		<title type="text">GMEV and CYC - The UKs Awful Recharging Network</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: CYC provided a response to this post. It can be found &lt;a href="/2015/07/response-from-cyc/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A little over three months ago, my wife and I took delivery of our new cars - Renault ZOEs. These are pure electric cars with 22kWh batteries that will do in the region of 90 miles on a full charge. We love our EVs and wouldn’t change them for anything, but we’ve had terrible experiences with the CYC network. Since we tell friends and family of all the positive things about EVs, it’s only fair we also share details of the bad things to ensure we’re not misleading anyone about what it’s like to own them.&lt;/p&gt;

&lt;p&gt;This is a long post; but that’s because there have been so many issues! :(&lt;/p&gt;

&lt;h2 id="sign-up-on-the-website-cant-login-to-app"&gt;Sign up on the website, can’t login to app…&lt;/h2&gt;

&lt;p&gt;I signed up with &lt;a href="http://chargeyourcar.org.uk/"&gt;CYC&lt;/a&gt; back in April. I signed up on the website, which allowed me to use an email address with a + in it (this is a perfectly valid character to use in an email address). When I came to install the app on my phone, I discovered that the app does not allow you to login with email addresses that contain plusses! This is ridiculous for two reasons; 1) plusses are valid and 2) there is no value in validating an email address prior to attempting to login. I reported this to CYC, asking them to either fix their app or update my email address, but was left unable to use their app until this was done. A week or so later, they replied to say they’d updated my email address (no word on them fixing the issue), and I was finally able to login.&lt;/p&gt;

&lt;h2 id="app-security-issues"&gt;App security issues&lt;/h2&gt;

&lt;p&gt;While waiting for CYC to update my email address (above), I made some attempts to login to their app by trying to guess what the issue might be. I guessed maybe they were failing to encode the email address properly when sending it to their server (such that the + may be interpreted as a space, as is possible with url decoding). I entered the url-encoded version of a plus, and the app gave me back an error message &lt;em&gt;with a stack trace&lt;/em&gt; from their server. This means there are certain characters in email addresses that &lt;em&gt;pass the (bad) validation&lt;/em&gt; yet cause server errors. This makes me suspicious that there is a security exploit hiding in here. I have reported this to CYC and had no response about it. This really makes me question how safe my card details are on their system.&lt;/p&gt;

&lt;h2 id="fraudulent-1-charges-from-cyc"&gt;Fraudulent £1 charges from CYC&lt;/h2&gt;

&lt;p&gt;While waiting for CYC to update my email address (above), we wanted to try using one of their chargers. Since my wife has an EV, she signed for a CYC account too. When we tried to use their charger in Birkenhead at the ferry terminal we found that she needed to add card details (despite the charger being free). She did so, and a few days later we discovered that the £1 pre-auth the app claimed to make was actually a real £1 charge. Whilst this is not a significant amount of money, it is unacceptable (and fraudulent) to take money from someones account like this. We reported this to CYC who acknowledged the issue, blamed PayPoint(!) and refunded it. I posted &lt;a href="https://speakev.com/threads/oops-cyc-charging-1-at-signup-supposed-to-be-pre-auth.8780/"&gt;on the SpeakEV forums&lt;/a&gt; in case others had had the same charges and despite several people quickly saying they had, it was a month before CYC finally acknowledged the issue and said they had refunded everyone affected.&lt;/p&gt;

&lt;h2 id="rfid-cards-attached-to-the-wrong-accounts"&gt;RFID cards attached to the wrong accounts&lt;/h2&gt;

&lt;p&gt;One of the responses in the thread I made about the £1 charges (above) had a link to &lt;a href="https://speakev.com/threads/cyc-free-charge-anywhere-is-over-for-one-person.4976/"&gt;this thread&lt;/a&gt; which talks of several people having had RFID cards linked to their accounts that they did not own. This suggests there is some flaw in the process of CYC sending RFID cards that might mean someone gets a card on your account, and you pay for their charging!&lt;/p&gt;

&lt;h2 id="unreliable-network-birkenhead-ferry-terminal"&gt;Unreliable network (Birkenhead Ferry Terminal)&lt;/h2&gt;

&lt;p&gt;This is something that will be come up several times in this post! When trying to start a charge the CYC servers failed to connect to the charge point. This left us looking at a “Trying to start charge…” screen in the app until it timed out. We tried this over and over. It took 20 minutes of faffing before we eventually got the charger to start. Some people said this could be the mobile connection but we had a full “H” data connection and were able to access other services fine (including full use of the CYC app, until we tried to start the charge).&lt;/p&gt;

&lt;h2 id="confusion-over-whether-fees-are-annual-or-one-off"&gt;Confusion over whether fees are annual or one-off&lt;/h2&gt;

&lt;p&gt;The &lt;a href="http://ev.tfgm.com/"&gt;GMEV Website&lt;/a&gt; says the fee for their card is £10 one-off (this is subsidised). The CYC website says the fee for their card is £20 annually. It seems strange that one would be one off and one annual, so I emailed both GMEV and CYC asking about this. GMEV (eventually, after 6 weeks) told me theirs is a one-off fee, while CYC didn’t actually answer the question. This makes me suspect CYC might be charging GMEV cardholders annually, despite not being what GMEV are advertising :(&lt;/p&gt;

&lt;h2 id="gmev-cards-not-available-to-people-who-live-near-manchester"&gt;GMEV cards not available to people who live near Manchester&lt;/h2&gt;

&lt;p&gt;Although CYC never answered my question about annual/one-off fee, they did tell me that I can’t have a GMEV card because I don’t live inside Manchester. This is despite living near Manchester (Cheshire) and visiting often. This seems a little backwards given that people in Manchester with EVs probably have home chargers and probably need the GMEV network less. Those that live around Manchester are probably those who would benefit most from GMEV chargers.&lt;/p&gt;

&lt;h2 id="incorrect-charger-location-hulme-street"&gt;Incorrect charger location (Hulme Street)&lt;/h2&gt;

&lt;p&gt;Once I finally had access to my CYC account via the app, we visited Manchester. We found a Hulme Street charger on their app and drove to the marked location on the map. It wasn’t there. Turned out that the location on their map was completely wrong, it was marked several streets away from where it had actually be installed! We headed to Hulme Street (by name) and eventually found the charger.&lt;/p&gt;

&lt;h2 id="no-charger-number-hulme-street"&gt;No charger number (Hulme Street)&lt;/h2&gt;

&lt;p&gt;In addition to using the RFID card or App, there is an option to pay by phone. To use this, you need the number of the charger which is (supposed to be) printed on the side of the charger. It is often not. The Hulme Street charger had big blank spaces where the numbers should be, making it impossible to start a charge by phone unless you can find the charger number (eg. via the app).&lt;/p&gt;

&lt;h2 id="unreliable-parking-fee-info-hulme-street"&gt;Unreliable parking fee info (Hulme Street)&lt;/h2&gt;

&lt;p&gt;The Hulme Street charger was marked as “free parking while charging” on the CYC website. When we got there we discovered that this wasn’t the case, and it was pay-and-display. The pay-and-display sign clearly mentioned the EV charger so it wasn’t just for non-charging vehicles.&lt;/p&gt;

&lt;h2 id="unreliable-network---no-charge-hulme-street"&gt;Unreliable network - no charge (Hulme Street)&lt;/h2&gt;

&lt;p&gt;After getting the kids out of the car and the pushchair unfolded, we tried for 30 minutes to get the Hulme Street charger to start a charge. It failed every time. Clearly the CYC network had no ability to connect to this charger. After 30 minutes, we gave up and left Hulme Street to find another charger. Because we had such little charge left we were concerned about getting home. Rather than driving around Manchester using up our remaining juice and having the same experience, we headed to the Trafford Centre (where we knew there were quite a few chargers) getting there with just 7 miles remaining charge!&lt;/p&gt;

&lt;h2 id="unreliable-network-trafford-centre"&gt;Unreliable network (Trafford Centre)&lt;/h2&gt;

&lt;p&gt;Once we arrived at the Trafford Centre, we spent another 20 minutes trying to start a charge. At one point, both my wife and I were trying to start charges from our own apps/logins on different sockets in the hope one would work. We did eventually get charging, but only after much frustration.&lt;/p&gt;

&lt;h2 id="no-charger-number-trafford-centre"&gt;No charger number (Trafford Centre)&lt;/h2&gt;

&lt;p&gt;Like Hulme Street, two ot the three charge posts at Trafford Centre have no numbers on them. It looks like they were written on with a marker pen which, unsurprisingly, has washed away in the rain. The only way we were able to start a charge was by guessing the numbers based on the one unit that them still visible (though the numbers go backwards, which confuses things further).&lt;/p&gt;

&lt;h2 id="unreliable-network-trafford-centre-again"&gt;Unreliable network (Trafford Centre… again)&lt;/h2&gt;

&lt;p&gt;A few weeks later we were were at the Traffod Centre again. There were no other EVs so we had a choice of three posts (6 sockets) to charge. We once again spent around 30 minutes trying to start a charge. We tried &lt;em&gt;all six&lt;/em&gt; sockets before we eventually got a charge to start, all because of the CYC servers failing to connect to the chargepoints.&lt;/p&gt;

&lt;h2 id="unreliable-network-mediacityuk"&gt;Unreliable network (MediaCityUK)&lt;/h2&gt;

&lt;p&gt;We were staying at MediaCityUK the weekend we visited the Traffod Centre. Their multistory car park has two GMEV/CYC chargers. Again, we had to try both sockets on each post before we eventually got one to start a charge. This process was repeated the following morning when we topped up.&lt;/p&gt;

&lt;h2 id="cyc-have-no-visibility-of-how-unreliable-their-chargers-are"&gt;CYC have no visibility of how unreliable their chargers are&lt;/h2&gt;

&lt;p&gt;After this latest trip, I contacted CYC on twitter (again) about this unreliability. They told me they couldn’t see &lt;em&gt;any&lt;/em&gt; failed charge attempts at either &lt;a href="https://twitter.com/ChargeYourCar/status/623873837701144576"&gt;Trafford Centre&lt;/a&gt; or &lt;a href="https://twitter.com/ChargeYourCar/status/623873583933227008"&gt;MediaCityUK&lt;/a&gt;. They believe that their system does show them, however it is quite clear that it does not (we tried over 10 times to start charges at the Trafford Centre). So clearly there is no visibility of failed charges (there should be; since it’s their servers failing to talk to the chargepoint). Wow.&lt;/p&gt;

&lt;h2 id="incorrect-charger-location-bury-market"&gt;Incorrect charger location (Bury Market)&lt;/h2&gt;

&lt;p&gt;Today we headed to Bury Market. The CYC website shows a charger at the market (Hilton Road). Where it’s marked, is actually a building. I went into this building and asked and was informed that the charger was actually in the market car park, some drive away. There is no way without help you’d be able to find this charger from the location on the CYC map (there’s a main road in between).&lt;/p&gt;

&lt;h2 id="unreliable-network-bury-market"&gt;Unreliable network (Bury Market)&lt;/h2&gt;

&lt;p&gt;Because of issues using the apps, we decided to try and use “pay by phone” today at Bury Market. I called and gave the charger number to the automated phone system and was told the charge had started. It took several minutes for the charger to receive the message to allow charging to start.&lt;/p&gt;

&lt;h2 id="unreliable-charger-bury-market"&gt;Unreliable charger (Bury Market)&lt;/h2&gt;

&lt;p&gt;Even after the charger said it was ready to start the charge, it took 10 minutes of plugging and unplugging before the charge finally locked the cable and allowed the car to start charging.&lt;/p&gt;

&lt;h2 id="unreliable-network-bury-market-1"&gt;Unreliable network (Bury Market)&lt;/h2&gt;

&lt;p&gt;When we returned to the car, we tried to stop the charge. We called the number again and it told us to wait while it stopped the charge. The charger did not get the signal… The automated phone system kept saying “Please wait…” for some time before it realised it had failed (our cable was still locked in the charge). It told use to press star if the cable had not unlocked. We did. It then said it was trying to restart the charger to unlock the cable. Unsurprisingly, that failed too. It told us if the cable was still not unlocked, we must call the helpline.&lt;/p&gt;

&lt;p&gt;We called the helpline… &lt;em&gt;IT WAS CLOSED ON A SATURDAY&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; Turns out it there is a number in the message if you keep listening to call for stuck cables. I missed this because it says “Thanks for calling CYC, our offices are now closed. Please leave a message after the tone. (pause, noise). If you have a stuck cable, call….”. So this was my fault, but the way the message is recorded really didn’t help.&lt;/p&gt;

&lt;p&gt;We are now stood, 50 miles from home, with our several-hundred-pound cable locked into a charger on what seems to be the worlds-most-unreliable charging network. Fuck.&lt;/p&gt;

&lt;p&gt;I thought I would call and try to stop the charge session again. Maybe this time it would work? I called the number. However, the system thinks I’ve already finished, so didn’t offer to stop my charge. It did offer to stop a session if I provided the last 4 digits of a card number used to start a session. However since the GEMV charger is free, there was no number I could give it that would match this session. In any case, I don’t think that would’ve worked as the system thought the session had finished.&lt;/p&gt;

&lt;p&gt;Fuck. I really cannot afford to leave this expensive cable in Bury!&lt;/p&gt;

&lt;p&gt;Then I had an idea - what happens if I start a new charge session, then end that? Maybe by fluke it will work? This time, I decided to try the app. Trying to start a session actually caused the cable to unlock, so I grabbed it.&lt;/p&gt;

&lt;p&gt;Phew!&lt;/p&gt;

&lt;p&gt;If this hadn’t had worked; I would’ve been pushing CYC to cover the cost of a replacement charge cable. To me, it is unacceptable to have a charger that can have a cable locked in, with no available helpline during a Saturday.&lt;/p&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;Our experience of the CYC network (mostly GMEV, but even non-GMEV chargers) has been poor. No other network has given us any issues like this. CYC keep telling me that an RFID card will solve these issues (no surprise - “give us money, that will make it better”), however I’m reluctant to pay £40/year (for the two of us) to find out if that’s true. We would happily pay £20 one-off for two GMEV cards, but alas, we don’t qualify, despite living just far enough from Manchester to need a charge if we visit.&lt;/p&gt;

&lt;p&gt;Others keep telling me CYC is fine, and these problems are not CYCs fault. However, these chargers are connected to their network. If CYC can’t monitor the reliability, nobody can. The fact that they don’t even have any visibility of failed charge attempts speaks volumes about how much they care about making their network reliable.&lt;/p&gt;

&lt;p&gt;I’d love to hear from others that use CYC (especially GMEV) chargers (in the comments, &lt;a href="https://plus.google.com/113181962167438638669/posts/Rd9j4JbRSFT"&gt;Google+&lt;/a&gt;,  &lt;a href="https://speakev.com/threads/gmev-and-cyc-the-uks-awful-recharging-network.10259/"&gt;SpeakEV&lt;/a&gt; or &lt;a href="https://www.reddit.com/r/electricvehicles/comments/3ekxdu/gmev_and_cyc_the_uks_awful_recharging_network/"&gt;Reddit&lt;/a&gt;). A few of these issues you could consider to just be hiccups but when we’ve had the same issues across 10 difference chargers across as many visits, it seems like a far more serious underlying problem.&lt;/p&gt;

&lt;p&gt;This doesn’t change our opinions of EVs in any way, but we will definitely avoid CYC chargers wherever we can, and be sure to always have a fallback. Poor charging infrastructure is one of the big hurdles to EV adoption, and CYC are making this worse, not better by giving people a false sense of security about being able to get a charge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: CYC provided a response to this post. It can be found &lt;a href="/2015/07/response-from-cyc/"&gt;here&lt;/a&gt;.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2015/07/gmev-and-cyc-the-uks-awful-recharging-network/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/dcPr7RGpg00" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/dcPr7RGpg00/" title="GMEV and CYC - The UKs Awful Recharging Network" />
	<feedburner:origLink>https://blog.dantup.com/2015/07/gmev-and-cyc-the-uks-awful-recharging-network/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-12-30:/2014/12/getting-email-notifications-of-xbox-live-games-with-gold-and-deals-with-gold/</id>
		<published>2014-12-30T00:00:00+00:00</published>
		<updated>2014-12-30T00:00:00+00:00</updated>
		
			<category term="Email" />
		
			<category term="Xbox" />
		
			<category term="Xbox Live" />
		
		<title type="text">How to Get Email Notifications of Xbox Live Games with Gold and Deals with Gold</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I recently picked up an Xbox One console (Gamertag is &lt;a href="https://account.xbox.com/Profile?Gamertag=DanTup"&gt;DanTup&lt;/a&gt;) and have been downloading the free &lt;a href="http://www.xbox.com/live/games-with-gold"&gt;Games with Gold&lt;/a&gt; titles and picked up a few of the &lt;a href="http://www.xbox.com/live/deals-with-gold"&gt;Deals with Gold&lt;/a&gt; offers.&lt;/p&gt;

&lt;p&gt;I looked around for some way of getting notifications of new deals/free games so that I wouldn’t miss anything if I was busy or didn’t play Xbox for a while, but couldn’t find anything. I did, however, discover that &lt;a href="http://majornelson.com/"&gt;Major Nelson&lt;/a&gt; posted about every deal/free game (and this month, also the Countdown to 2015 deals). He also has RSS feeds for &lt;a href="http://feeds.feedburner.com/xboxlivemarketplace"&gt;all marketplace posts&lt;/a&gt; or &lt;a href="http://majornelson.com/category/deal/feed/"&gt;all posts tagged ‘deal’&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did a bit of searching online, and found a couple of free RSS-to-Email services. Some of them looked a bit naff and some wouldn’t let me sign up with a plus symbol in my address (urgh!). I found two that looked reasonable and didn’t have poor email validation but since I didn’t know how reliable they’d be, I decided to sign up for both and see how they went.&lt;/p&gt;

&lt;p&gt;The two services I signed up with are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href="https://blogtrottr.com/"&gt;Blogtrottr&lt;/a&gt;&lt;/strong&gt; - Free with ads; or paid plans are available.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href="https://feedrabbit.com/"&gt;Feedrabbit&lt;/a&gt;&lt;/strong&gt; - Free with no ads, but restricted to 10 RSS feeds and 20 emails/day max; or paid plan available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was pleasantly surprised that both services have worked perfectly well since I signed up at the start of December, which is around 17 emails so far (they’re currently arriving daily, due to the Countdown to 2015 deals). The emails tend to arrive within an hour of each other; and according to the timestamps; actually arrive before Major Nelson even posts them! (Timezones FTW).&lt;/p&gt;

&lt;p&gt;As mentioned above; Blogtrottr does include ads; but other than that, there’s actually &lt;em&gt;very&lt;/em&gt; little difference in the two emails:&lt;/p&gt;

&lt;p&gt;&lt;a href="/post_images/major_nelson_rss_email.png"&gt;&lt;img src="/post_images/major_nelson_rss_email.png" alt="Comparison of emails from Blogtrottr and Feedrabbit" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the emails always arrive together and it’s such little effort to archive them; I’m going to keep both services active (reducing the chances I might miss one if a service disappears overnight). Although you might prefer Feedrabbit’s ad-free emails, it could be argued that Blogtrottr’s ad-sponsored version might be more sustainable. Since both have been completely reliable, I can’t really offer any additional information that might swing your choice one way or the other.&lt;/p&gt;

&lt;p&gt;If you know of any other services that work well; or have any comments on these two; feel free to post in the comments!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/12/getting-email-notifications-of-xbox-live-games-with-gold-and-deals-with-gold/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/zDc4IkYZuiI" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/zDc4IkYZuiI/" title="How to Get Email Notifications of Xbox Live Games with Gold and Deals with Gold" />
	<feedburner:origLink>https://blog.dantup.com/2014/12/getting-email-notifications-of-xbox-live-games-with-gold-and-deals-with-gold/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-11-06:/2014/11/using-inbox-by-gmail-from-a-google-apps-or-non-gmail-email-account/</id>
		<published>2014-11-06T00:00:00+00:00</published>
		<updated>2014-11-06T00:00:00+00:00</updated>
		
			<category term="Email" />
		
			<category term="Google Apps" />
		
			<category term="Android" />
		
		<title type="text">Using Inbox by Gmail with IMAP or from a Google Apps or non-Gmail email account</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;If you’re looking for IMAP support&lt;/strong&gt; (lots of search terms suggest this post is getting a lot of traffic for this) then you can just use the same IMAP details you do for Gmail (&lt;a href="https://support.google.com/mail/answer/7126229?hl=en"&gt;see here&lt;/a&gt;). Inbox is just another frontend onto your Gmail account, you can even switch between the two as you like!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google recently launched &lt;a href="http://www.google.com/inbox"&gt;Inbox by Gmail&lt;/a&gt;. Like all new Google Products, if you’re a Google Apps user (whether it be a legacy free account, or as a paying business), you’ve been excluded with no indication from Google for how long.&lt;/p&gt;

&lt;p&gt;After much faffing about; I was able to set Inbox up to work with my Google Apps account; including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Emails sent to my Google Apps address show up in Inbox&lt;/li&gt;
  &lt;li&gt;Replies sent from Inbox show my Google Apps address as the sender&lt;/li&gt;
  &lt;li&gt;Replies sent from Inbox show up in the Sent folder in my &lt;em&gt;Google Apps&lt;/em&gt; account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In theory; this should work with both Google Apps accounts and any other email account that you can access via IMAP/POP and SMTP.&lt;/p&gt;

&lt;h2 id="you-will-need"&gt;You will need&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;A Gmail account that has been invited to Inbox&lt;/li&gt;
  &lt;li&gt;An email account you would like to use with Inbox that is not a Gmail account that you have IMAP/POP and SMTP access to&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="step-1-forward-your-email-to-the-gmail-account"&gt;Step 1: Forward your email to the Gmail account&lt;/h2&gt;

&lt;p&gt;If you’re using a Google Apps account, this is done in Settings -&amp;gt; Forwarding and POP/IMAP. If you’re using a non-Google account, this may vary. If forwarding from a Google Apps account, your Gmail account will be sent an email to click to confirm the address.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/inbox-1-forward.png" alt="Forward your email to the Gmail account" /&gt;&lt;/p&gt;

&lt;p&gt;With this done; new email sent to you will turn up in Inbox.&lt;/p&gt;

&lt;h2 id="step-2-set-up-your-outgoing-email-address-in-gmail"&gt;Step 2: Set up your outgoing email address in Gmail&lt;/h2&gt;

&lt;p&gt;Login to your Gmail account, and in Settings -&amp;gt; Accounts and Import you’ll need to set up a “Send mail as” address for your primary email address.&lt;/p&gt;

&lt;p&gt;Be sure to tick “Send as Alias”; as this will stop Inbox from including yourself in the To field of all replies.&lt;/p&gt;

&lt;p&gt;It is important that you choose the option to send mail through your own SMTP server. If using a Google Apps account; you’ll want to use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Server: smtp.gmail.com, Port: 587&lt;/li&gt;
  &lt;li&gt;Username: Your full Google Apps email address&lt;/li&gt;
  &lt;li&gt;Password: Your full Google Apps email password; or if you have two-factor auth on, an Application-Specific password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/post_images/inbox-2-send-mail-as.png" alt="Set up your outgoing email address in Gmail" /&gt;&lt;/p&gt;

&lt;p&gt;This allows your Gmail account to send email via your primary accounts SMTP server.&lt;/p&gt;

&lt;h2 id="step-3-mark-your-new-outgoing-address-as-default"&gt;Step 3: Mark your new outgoing address as default&lt;/h2&gt;

&lt;p&gt;&lt;img src="/post_images/inbox-3-default-email-address.png" alt="Mark your new outgoing address as default" /&gt;&lt;/p&gt;

&lt;p&gt;This will ensure any new emails you create in Inbox (with the Compose option) will come from your primary email address.&lt;/p&gt;

&lt;h2 id="step-4-ensure-replies-are-sent-from-the-email-address-that-received-them"&gt;Step 4: Ensure replies are sent from the email address that received them&lt;/h2&gt;

&lt;p&gt;&lt;img src="/post_images/inbox-4-reply-from-same.png" alt="Ensure replies are sent from the email address that received them" /&gt;&lt;/p&gt;

&lt;p&gt;This will ensure when you reply to an email in Inbox, the senders address is set to the same outgoing address that it was addressed to (and in the case of your primary email account, that means the email is sent via the configured SMTP server, which is what causes it to show up in the Sent folder of your Google Apps account).&lt;/p&gt;

&lt;h2 id="step-5-clear-your-inbox-by-gmail-app-data"&gt;Step 5: Clear your Inbox by Gmail app data&lt;/h2&gt;

&lt;p&gt;This step might be important. I didn’t do this the first time I tried this; and Inbox didn’t honour my outgoing email address settings (I believe it had cached the fact that I didn’t have any alternative outgoing email addresses). After clearing the data; Inbox correctly let me pick the outgoing email address, and defaulted using the settings I’d set above.&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/inbox-5-clear-app.png" alt="Clear your Inbox by Gmail app data" /&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/11/using-inbox-by-gmail-from-a-google-apps-or-non-gmail-email-account/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/ULDdW_kQvXk" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/ULDdW_kQvXk/" title="Using Inbox by Gmail with IMAP or from a Google Apps or non-Gmail email account" />
	<feedburner:origLink>https://blog.dantup.com/2014/11/using-inbox-by-gmail-from-a-google-apps-or-non-gmail-email-account/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-10-29:/2014/10/have-the-angular-team-lost-their-marbles/</id>
		<published>2014-10-29T00:00:00+00:00</published>
		<updated>2014-10-29T00:00:00+00:00</updated>
		
			<category term="JavaScript" />
		
			<category term="Open Source" />
		
		<title type="text">Have the Angular Team lost their marbles?</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I’ve &lt;a href="/2014/08/you-have-ruined-html/"&gt;posted before&lt;/a&gt; that I’m not a big fan of frameworks like Angular. Trying to re-invent programming in declarative HTML tags/attributes/{{ curlies }} will likely &lt;a href="http://gbracha.blogspot.co.uk/2014/09/a-domain-of-shadows.html"&gt;always feel lacking&lt;/a&gt;; and every framework having its own unique way of doing this means a ton of effort needs to be &lt;del&gt;wasted&lt;/del&gt;&lt;ins&gt;invested&lt;/ins&gt; in updating tooling to support/understand it.&lt;/p&gt;

&lt;p&gt;So, with my pre-existing bias out of the way; I have to say that recent information about Angular’s future only makes me hold my head in my hands even more…&lt;/p&gt;

&lt;h2 id="not-just-one-new-concept-to-add-to-tooling-but-three"&gt;Not just one new concept to add to tooling; but three!&lt;/h2&gt;

&lt;p&gt;It wasn’t enough that tooling needed to be updated to support Angular; it seems that the Angular Team now want tooling to support:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Angular v1 syntax&lt;/li&gt;
  &lt;li&gt;Angular v2 syntax (this appears to be &lt;a href="http://jaxenter.com/angular-2-0-112094.html"&gt;significantly different&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://docs.google.com/document/d/11YUzC-1d0V1-Q3V0fQ7KSit97HnZoKVygDxpWzEYW0U/mobilebasic?pli=1&amp;amp;viewopt=127"&gt;AtScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="different-but-no-better"&gt;Different, but no better&lt;/h2&gt;

&lt;p&gt;In &lt;a href="http://jaxenter.com/angular-2-0-112094.html"&gt;this article&lt;/a&gt; there’s a comparison of code for Angular v1 and Angular v2; that looks like this:&lt;/p&gt;

&lt;h3 id="angular-v1"&gt;Angular v1&lt;/h3&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ng-controller=&lt;/span&gt;&lt;span class="s"&gt;"SantaTodoController"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;ng-model=&lt;/span&gt;&lt;span class="s"&gt;"newTodoTitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;ng-click=&lt;/span&gt;&lt;span class="s"&gt;"addTodo()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;tab-container&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;tab-pane&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Tobias"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ng-repeat=&lt;/span&gt;&lt;span class="s"&gt;"todo in todosOf('tobias')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;ng-model=&lt;/span&gt;&lt;span class="s"&gt;"todo.done"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				{{todo.title}}&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;ng-click=&lt;/span&gt;&lt;span class="s"&gt;"deleteTodo(todo)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;tab-pane&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;/tab-container&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id="angular-v2"&gt;Angular v2&lt;/h3&gt;
&lt;p&gt;(Note: I’ve tweaked the HTML slightly to what I believe it should be; the article seemed to have some issues).&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;]="&lt;/span&gt;&lt;span class="na"&gt;newTodoTitle&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)="&lt;/span&gt;&lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="err"&gt;()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;tab-container&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;tab-pane&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Good kids"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;ng-repeat&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;]="&lt;/span&gt;&lt;span class="na"&gt;todosOf&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;good&lt;/span&gt;&lt;span class="err"&gt;')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;]="&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				{{todo.title}}&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)="&lt;/span&gt;&lt;span class="na"&gt;deleteTodo&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;tab-pane&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;/tab-container&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are some significant differences in how Angular concepts are marked up here; but are they any better? Really? Previously things were embedded in strings in attributes, which was bad enough. But in Angular v2, we’re now using [brackets], (parens) and pipes in attribute names too. It’s different, but I really don’t think it is better.&lt;/p&gt;

&lt;h2 id="not-ready-until-2016"&gt;Not ready until 2016&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Angular is aiming for a release by the end of 2015 - but early 2016 seems more realistic given the drastic changes that are planned&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;http://jaxenter.com/angular-2-0-112094.html&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is another big WTF. The changes are so significant that they can’t be progressively released. This makes the v1 -&amp;gt; v2 migration seem like it’s going to be an incredible amount of work.&lt;/p&gt;

&lt;h2 id="angular-13-eol-after-just-18-24-months"&gt;Angular 1.3 EOL after just 18-24 months&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;According to Brad Green of Angular, Angular 1.3 will continue to receive bugfix and security patch support for 18-24 months after the release of version 2.0.&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;http://jaxenter.com/angular-2-0-112094.html&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This whole thing is starting to sound like Python 2.7 vs 3.0. Python 3.0 was released in 2008, and the change was so big and breaking, that Python 2.7 end-of-life is currently slated for &lt;strong&gt;2020&lt;/strong&gt;. That’s a 12 year cross-over. The Angular Team expect to give just 18-24 months. I don’t know which I think is more insane; supporting an old version of your product for 12 years, or giving just 18-24 months for people to migrate; both of them seem bat-shit-crazy to me.&lt;/p&gt;

&lt;h2 id="conclusion-dont-pick-angular"&gt;Conclusion: Don’t pick Angular&lt;/h2&gt;

&lt;p&gt;Where I work; we’re currently evaluating a bunch of new technologies/frameworks/etc. to see which might be suitable for use on vNext of our products. Amongst those being prototyped are Dart, React, Angular and a few others. With all of this info on Angular; I’m struggling to see how we could possibly pick it. Our current codebase has parts that are over 10 years old; and we hope our new codebase will last this long too. It seems that if we start writing Angular today; we’ll be forced to rewrite the frontend in three to four years at latest (and with the way apps are going, the frontend is likely to be a large codebase). This doesn’t sound very attractive.&lt;/p&gt;

&lt;p&gt;Maybe I’ve misunderstood something; but I’m really struggling to see a world in which this all makes good sense.&lt;/p&gt;

&lt;p&gt;We need frameworks that are stable and supported long-term; not that are constantly inventing new concepts and being rewritten with breaking changes every 5 minutes. Of everyone, Google should know how hard it is to maintain large web apps :-/&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/10/have-the-angular-team-lost-their-marbles/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/OfyeZLSZZdE" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/OfyeZLSZZdE/" title="Have the Angular Team lost their marbles?" />
	<feedburner:origLink>https://blog.dantup.com/2014/10/have-the-angular-team-lost-their-marbles/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-09-16:/2014/09/dart-vm-on-a-chromebook-without-linux/</id>
		<published>2014-09-16T00:00:00+00:00</published>
		<updated>2014-09-16T00:00:00+00:00</updated>
		
			<category term="Chromebook" />
		
			<category term="Dart" />
		
		<title type="text">Running the Dart VM on a Chromebook in Developer Mode (without installing Linux)</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;blockquote&gt;
  &lt;p&gt;Writing Dart? Check out &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;Dart Code, my Dart extension for Visual Studio Code!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I recently picked up a &lt;a href="https://www.chromebookchart.com/"&gt;Chromebook&lt;/a&gt; (a Dell Chromebook 11) and hoped to do a a little bit of hacking on Dart with it. Out-of-the-box the Chromebook can’t run native Dart code (as command line, nor in the browser), so you’re limited to web-based apps using Chrome Dev Editor (which is rather slow at compiling Dart in its JavaScript form).&lt;/p&gt;

&lt;p&gt;In order to get around this, many people install Linux (usually Ubuntu via something like Crouton) but I didn’t want to do this because I don’t really like Ubuntu, and it seemed like quite an overhead just to run the (fairly light) Dart VM!&lt;/p&gt;

&lt;p&gt;So, when my (replacement!) Chromebook (my 3rd in two weeks!) turned up today; I thought I’d see if the Dart VM would work in Chrome OS’s &lt;code class="highlighter-rouge"&gt;Developer Mode&lt;/code&gt; without having to resort to Linux.&lt;/p&gt;

&lt;p&gt;It turned out to actually be pretty simple; and it might be even simpler to someone that understands Linux better than I do! The steps I followed are below. If anyone can improve these; please do post in the comments so I can update them. The editing of the &lt;code class="highlighter-rouge"&gt;bash_profile&lt;/code&gt; is a bit clunky (mainly because I don’t know &lt;code class="highlighter-rouge"&gt;vi&lt;/code&gt;; and I didn’t want to use another Chrome extension just to edit the file!) and I don’t really understand why I need to remount.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href="https://sites.google.com/site/chromeoswikisite/home/what-s-new-in-dev-and-beta/developer-mode"&gt;Enable developer mode&lt;/a&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: This will delete all of your locally stored data!&lt;/li&gt;
      &lt;li&gt;Every time you boot your device in this mode, you will get a &lt;a href="https://sites.google.com/site/chromeoswikisite/home/what-s-new-in-dev-and-beta/developer-mode"&gt;scary splash screen&lt;/a&gt; and have to press Ctrl+D not to reset your device&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Download the &lt;a href="https://www.dartlang.org/tools/download.html#a_la_carte"&gt;Dart SDK&lt;/a&gt; to your Downloads folder&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;I picked the 64bit version for my &lt;a href="https://www.chromebookchart.com/"&gt;Dell Chromebook 11&lt;/a&gt;; some Chromebooks might require the 32 bit version&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unzip the SDK to your downloads folder&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;I put this at &lt;code class="highlighter-rouge"&gt;~/Downloads/dart-sdk/&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Switch to the terminal window by pressing &lt;code class="highlighter-rouge"&gt;Ctrl+Alt+=&amp;gt;&lt;/code&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;(&lt;code class="highlighter-rouge"&gt;=&amp;gt;&lt;/code&gt; being the forward button where F2 would normally be)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run &lt;code class="highlighter-rouge"&gt;chmod +x ~/Downloads/dart-sdk/bin/dart&lt;/code&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;This will mark &lt;code class="highlighter-rouge"&gt;dart&lt;/code&gt; as executable&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run &lt;code class="highlighter-rouge"&gt;chmod +x ~/Downloads/dart-sdk/bin/pub&lt;/code&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;This will mark &lt;code class="highlighter-rouge"&gt;pub&lt;/code&gt; as executable&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run &lt;code class="highlighter-rouge"&gt;echo "sudo mount -i -o remount,exec /home/chronos/user/" &amp;gt;&amp;gt; ~/.bash_profile&lt;/code&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;I’m not a Linux guy; but this seems to be required to allow you to execute anything on this drive&lt;/li&gt;
      &lt;li&gt;Without this, you’ll get &lt;code class="highlighter-rouge"&gt;Permission denied&lt;/code&gt; trying to execute anything&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run &lt;code class="highlighter-rouge"&gt;echo "PATH=\$PATH:~/Downloads/dart-sdk/bin/" &amp;gt;&amp;gt; ~/.bash_profile&lt;/code&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;This adds the Dart &lt;code class="highlighter-rouge"&gt;bin&lt;/code&gt; folder to your path so you can just type &lt;code class="highlighter-rouge"&gt;dart&lt;/code&gt; without the full path&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reboot your Chromebook&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;This isn’t strictly required, you could just re-run &lt;code class="highlighter-rouge"&gt;.bash_profile&lt;/code&gt;, but it’s a good way to ensure it works after a reboot&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now when you want to run dart, you can do it from the normal browser-based shell!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Press &lt;code class="highlighter-rouge"&gt;Ctrl + Alt + T&lt;/code&gt; to open a the ChromeOS Terminal&lt;/li&gt;
  &lt;li&gt;Type &lt;code class="highlighter-rouge"&gt;shell&lt;/code&gt; to switch to the proper shell&lt;/li&gt;
  &lt;li&gt;Type &lt;code class="highlighter-rouge"&gt;dart&lt;/code&gt; or &lt;code class="highlighter-rouge"&gt;pub&lt;/code&gt; to execute dart/pub&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a screenshot showing it works on my Chromebook; I’m using &lt;a href="https://chrome.google.com/webstore/detail/chrome-dev-editor-develop/pnoffddplpippgcfjdhbmhkofpnaalpg?hl=en"&gt;Chrome Dev Editor&lt;/a&gt; for the Dart editing, and executing the tests in the shell. You can run &lt;code class="highlighter-rouge"&gt;pub serve&lt;/code&gt; this way and hit up &lt;code class="highlighter-rouge"&gt;http://localhost:8080/&lt;/code&gt; in the browser for web apps (which I suspect will compile much quicker than the Chrome Dev Editor version).&lt;/p&gt;

&lt;p&gt;&lt;img src="/post_images/dart_chromebook.png" alt="Dart VM on Chromebook in Developer Mode" /&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in a Chromebook; don’t forget to take a look at &lt;a href="https://www.chromebookchart.com/"&gt;Chromebook Comparison Chart&lt;/a&gt;! :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/09/dart-vm-on-a-chromebook-without-linux/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/d6CKKOgE3D0" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/d6CKKOgE3D0/" title="Running the Dart VM on a Chromebook in Developer Mode (without installing Linux)" />
	<feedburner:origLink>https://blog.dantup.com/2014/09/dart-vm-on-a-chromebook-without-linux/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-08-30:/2014/08/chromebook-comparison-chart/</id>
		<published>2014-08-30T00:00:00+00:00</published>
		<updated>2014-08-30T00:00:00+00:00</updated>
		
			<category term="Chromebook" />
		
		<title type="text">Chromebook Comparison Chart</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I’ve recently been looking for a new Chromebook. My Nexus 7 tablet just doesn’t cut it for a lot of things, and I’d like to not have to boot my desktop for some of these (relatively simply) tasks.&lt;/p&gt;

&lt;p&gt;I’m aware there are some new Chromebooks being released that are fanless and more powerful and have better battery life than previous versions (such as i3 and Tegra processors) so I started looking online.&lt;/p&gt;

&lt;p&gt;I was surprised (and disappointed) that there aren’t any good places that compare hardware in all of the Chromebooks, despite the hardware being the only thing that’s really different with Chromebooks! I’m a bit picky, and definitely want my Chromebook to have a higher-than-1366-768 screen resolution and at least 4GB RAM and it was hard to just find a list of the devices that met these requirements.&lt;/p&gt;

&lt;p&gt;I started compiling a table of all the Chromebooks I could find, and kinda got carried away. I thought it was worth posting it online so that others can sort/filter/compare Chromebook models more easily!&lt;/p&gt;

&lt;div style="text-align: center; font-size: 25px;"&gt;
&lt;a href="https://www.chromebookchart.com/"&gt;
	Google Chromebook Comparison Chart&lt;br /&gt;
	&lt;img src="http://www.google.com/chrome/assets/common/images/devices/chromebook-family-homepage-promo.jpg" /&gt;

&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;If you find any mistakes or anything missing; please let me know! You can contact me in various ways from the buttons across the top of this blog.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/08/chromebook-comparison-chart/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/Di8Y2hJlOiA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/Di8Y2hJlOiA/" title="Chromebook Comparison Chart" />
	<feedburner:origLink>https://blog.dantup.com/2014/08/chromebook-comparison-chart/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-08-25:/2014/08/you-have-ruined-html/</id>
		<published>2014-08-25T00:00:00+00:00</published>
		<updated>2014-08-25T00:00:00+00:00</updated>
		
			<category term="JavaScript" />
		
			<category term="Dart" />
		
			<category term="Open Source" />
		
		<title type="text">You have ruined HTML</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Not content with &lt;a href="http://codeofrob.com/entries/you-have-ruined-javascript.html"&gt;ruining JavaScript&lt;/a&gt;, it seems you’re set on ruining HTML too.&lt;/p&gt;

&lt;p&gt;If you ask people what they’re building their client-side web apps in today; chances are they’ll tell you some hipster JavaScript framework that could be found listed on the &lt;a href="http://todomvc.com/"&gt;TodoMVC&lt;/a&gt; website. The most popular ones tend to be Angular, Ember, Knockout, Backbone and more recently, Polymer.&lt;/p&gt;

&lt;p&gt;Most of these frameworks have this “great” feature that lets you “easily” reference data in your views using binding expressions. They look something like this:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="c"&gt;&amp;lt;!-- Knockout --&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;First name: &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;"value: firstName"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Last name: &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;"value: lastName"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Hello, &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;"text: fullName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;!&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c"&gt;&amp;lt;!-- Angular --&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phones"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;ng-repeat=&lt;/span&gt;&lt;span class="s"&gt;"phone in phones | filter:query | orderBy:orderProp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{phone.name}}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{phone.snippet}}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c"&gt;&amp;lt;!-- Ember --&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Name:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	{{input type="text" value=name placeholder="Enter your name"}}&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My name is {{name}} and I want to learn Ember!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Although the syntax varies slightly; they’re all doing similar things. They give the ability to embed some expression into your HTML that is “magically processed” by the framework. This means no manual DOM manipulation; you can write nicely formed (ish) HTML inside these expressions and the framework will do all the mundane tasks like creating nodes, wiring up event handlers and updating the DOM when your model changes. This all sounds like a nice convenience, it’s not hard to see why everyone is jumping on the bandwagon and developing this way.&lt;/p&gt;

&lt;p&gt;However; turns out that it’s not all as advertised; there are some significant flaws in this approach that often go unnoticed until you’re trying to do something complicated.&lt;/p&gt;

&lt;p&gt;Where I work, we’re currently building some prototypes in many of these new technologies to help guide our decision on our technologies for new versions of our products. It seems that each dev using one of these fancy frameworks is struggling and we spend a lot of time debugging issues or scouring StackOverflow for how to do (what we’d consider) basic tasks.&lt;/p&gt;

&lt;p&gt;Some of the frustrations include…&lt;/p&gt;

&lt;h3 id="no-help-from-your-tools"&gt;No help from your tools&lt;/h3&gt;

&lt;p&gt;Because these binding expressions aren’t real HTML constructs, you’ll get no help from your tooling with them. No highlighting; no code-completion; no tooltips; no static analysis or type-checking. As far as your HTML editor is concerned, these expressions are just strings.&lt;/p&gt;

&lt;h3 id="simple-tags-are-never-simple"&gt;Simple tags are never simple&lt;/h3&gt;

&lt;p&gt;You’ll start off binding properties like {{ name }}, but you’ll soon find your expressions growing into complicated messes. You’ll find the built-in functionality doesn’t quite do what you need and you’ll start having to bend the framework with &lt;a href="http://stackoverflow.com/a/21492524"&gt;crap like this&lt;/a&gt;, writing a JavaScript function that returns a function simply to apply a filter with a dynamic name:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="na"&gt;ng-show=&lt;/span&gt;&lt;span class="s"&gt;"banners"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="na"&gt;ng-repeat=&lt;/span&gt;&lt;span class="s"&gt;"banner in banners.data"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;ng-repeat=&lt;/span&gt;&lt;span class="s"&gt;"col in banner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		{{ col | dynamicFilter : banners.cols[$index].formatter }}&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nx"&gt;myApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dynamicFilter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filterName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Formatters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filterName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Formatters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filterName&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="k"&gt;else&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;				&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="p"&gt;};&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="p"&gt;});&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id="nothing-ever-works-first-time"&gt;Nothing ever works first time&lt;/h3&gt;

&lt;p&gt;As your binding expressions get more complicated, you’ll find that they rarely work first time. You’ll end up playing the shotgun-debugging game of tweaking your expression and reloading to see if you got it right (this will really suck if you have a long cycle to get back into the correct position for testing; such as having to manually log in to your app). For example, in Knockout, observables are functions, so when you want values you often need to add parens to the property. However sometimes your properties are not observable, so sometimes you don’t.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="c"&gt;&amp;lt;!-- Which ones need parens()? --&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;"value: filterData.DateCriteria().DateFrom"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"DateFrom"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id="error-messages-rarely-exist-and-are-frequently-useless"&gt;Error messages rarely exist and are frequently useless&lt;/h3&gt;

&lt;p&gt;When you get your binding expressions wrong; you’ll frequently see &lt;em&gt;nothing&lt;/em&gt;. If you’re lucky, you might get a JavaScript error. If you do, it’ll probably say something like &lt;code class="highlighter-rouge"&gt;e is not a function&lt;/code&gt; and give you a stack trace that’s a hundred frames deep inside your framework and no part of it points at your HTML binding expression.&lt;/p&gt;

&lt;h3 id="debugging-is-near-impossible"&gt;Debugging is near impossible&lt;/h3&gt;

&lt;p&gt;You can’t put a breakpoint on a {{ stupid binding expression }} because it’s not JavaScript (or is it?). You’ll often end up pulling them out into “computed properties” or some equivalent, but now your JavaScript is filling up with extension functions on objects that make no sense outside of framework-specific view-funkiness. All because your framework has a half-arsed parsing of JavaScript expressions for bindings.&lt;/p&gt;

&lt;h2 id="whats-the-alternative"&gt;What’s the alternative?&lt;/h2&gt;

&lt;p&gt;Simple. &lt;em&gt;Do your programming in a programming language&lt;/em&gt;. Don’t try to embed it in some crazy will-never-be-a-standard binding expression invented by a fly-by-night JavaScript framework.&lt;/p&gt;

&lt;p&gt;You probably don’t want to go back to completely manipulating the DOM by hand, but there are options like &lt;a href="http://facebook.github.io/react/"&gt;React&lt;/a&gt; (without JSX, since JSX sucks for many of the same reasons above) which avoids DOM manipulation (instead favouring the idea of just describing the “current state”) and keeps all your code as code. This way, your existing tools, linters, coverage etc. will all work fine. Want to extract a function to reuse it? No problem! Want to refactor-&amp;gt;rename? No problem (well, ish… as well as this works in JS!). Want to use TypeScript and have type-checking in your expressions? No problem (well, ish… when TypeScript+React play nicer together ;)).&lt;/p&gt;

&lt;p&gt;At work, one of our devs is building a prototype being built in React, and I’m also building one in Dart (in a React-inspired, code-generating elements way, while I quietly hope &lt;a href="https://github.com/google/dart-tagtree"&gt;Dart TagTree&lt;/a&gt; evolves into a usable library). Neither of these prototypes are hitting the same frustrations as those that encourage {{ silly binding expressions }} in HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML is a declarative markup language. Let’s leave it as such, and stop trying to crowbar non-standard binding expressions into it.&lt;/strong&gt;&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/08/you-have-ruined-html/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/6Ul-1_EghUA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/6Ul-1_EghUA/" title="You have ruined HTML" />
	<feedburner:origLink>https://blog.dantup.com/2014/08/you-have-ruined-html/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-08-17:/2014/08/jawbone-up24-vs-fitbit-flex/</id>
		<published>2014-08-17T00:00:00+00:00</published>
		<updated>2014-08-17T00:00:00+00:00</updated>
		
			<category term="Health" />
		
			<category term="Fitness Trackers" />
		
		<title type="text">Jawbone Up24 vs Fitbit Flex</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I previously blogged about &lt;a href="/2014/06/why-i-returned-my-jawbone-up24-after-just-one-week/"&gt;my negative experience&lt;/a&gt; with Jawbone’s Up24, which resulted in exchanging the &lt;a href="http://prodct.info/up24"&gt;Jawbone Up24&lt;/a&gt; for a &lt;a href="http://prodct.info/flex"&gt;Fitbit Flex&lt;/a&gt;. I’ve had the Flex for around 6 weeks now (my wife has had hers for about 4); so I thought it was worth posting a summary of my thoughts on the two devices compared.&lt;/p&gt;

&lt;h2 id="jawbone-up24-pros"&gt;Jawbone Up24 Pros&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;“Intelligent” Alarm: You provide a window in which you’d like to be woken up, and the Up/Up24 will aim to wake you up when you’re in the lightest sleep during that window (which is intended to make you feel less tired upon waking).&lt;/li&gt;
  &lt;li&gt;Idle Alert: Set a maximum time for which you’d like to be idle; and the Up/Up24 will vibrate to remind you to get up and move.&lt;/li&gt;
  &lt;li&gt;The mobile app is colourful and well designed.&lt;/li&gt;
  &lt;li&gt;Food logging in the app is &lt;em&gt;reasonably&lt;/em&gt; good.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="jawbone-up24-cons"&gt;Jawbone Up24 Cons&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Battery drain is absolutely poor; my Nexus 4 went from lasting 2 entire days to far less than a day. This is ultimately what caused me to return it; I’m not living with having to charge my phone more than once a day.&lt;/li&gt;
  &lt;li&gt;The mobile app is very slow/sluggish (the Nexus 4 isn’t the fastest Android phone on the market; but it’s also &lt;em&gt;far&lt;/em&gt; from slowest).&lt;/li&gt;
  &lt;li&gt;Food logging had some glaring bugs (drinks often logged as “meals”, no reasonable way to save your own meals and easily reuse them later).&lt;/li&gt;
  &lt;li&gt;Band is rather uncomfy; it’s bulky on the bottom of your wrist (bad if you’re a programmer like me, wrists frequently on the desk) and the size isn’t adjustable for different size wrists, you have to pick the “closest fit” from the available sizes.&lt;/li&gt;
  &lt;li&gt;Requires smartphone app; no ability to log or view data from a computer.&lt;/li&gt;
  &lt;li&gt;More expensive than the Fitbit Flex!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="fitbit-flex-pros"&gt;Fitbit Flex Pros&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Band is much comfier; and all devices come with two sizes of adjustable bands, so you don’t need to “pick the closest” at time or purchase.&lt;/li&gt;
  &lt;li&gt;Flex comes with a USB dongle that allows syncing without a computer.&lt;/li&gt;
  &lt;li&gt;Decent web app to allow viewing/logging of data directly from your computer.&lt;/li&gt;
  &lt;li&gt;Food logging prioritises items you create/favourite in the autocomplete list.&lt;/li&gt;
  &lt;li&gt;Battery lasts around a week (usually… see Cons).&lt;/li&gt;
  &lt;li&gt;Cheaper than the Up24!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="fitbit-flex-cons"&gt;Fitbit Flex Cons&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Periodically, the Flex will completely die with no warning/notifications. Battery can be 80%, and then device just goes dead. It’s possible it just “crashes”, but when charging, it always starts at 1 light. We’ve had 4 Flexes between us, and all 4 have done it (though the most recent two only once or twice in almost 6 weeks).&lt;/li&gt;
  &lt;li&gt;Doesn’t log steps at all accurately when pushing a pushchair (I read both bands were supposed to be quite good at this; but we’re seeing probably half the steps we’d expect).&lt;/li&gt;
  &lt;li&gt;Sleep logging seems way off for me (I’m a bit of an insomniac). In “standard” mode, it says I sleep 7-8 hours when I know for a fact I didn’t. In “sensitive” mode, it logs 2-4 hours each night, in a pattern that doesn’t match the sleep I believe I get. I guess this is just down to the way it has to work (assuming still is asleep, even though when trying to fall asleep, we’re usually still!).&lt;/li&gt;
  &lt;li&gt;Support is a bit hit-and-miss. I’ve been round in circles with them trying to get Facebook/Twitter weekly summaries working, and I was asked to “re-link” both accounts 4 times with their shotgun-debugging. I also asked why the “After Dinner” option is missing from Android; and have had 4 responses so far, and I’m still not sure whether it will ever be fixed (they say it’s not a bug, but expected behaviour, but they’ve failed to explain how this could be expected).&lt;/li&gt;
  &lt;li&gt;Failing (and expensive-to-replace) Bands: Although we haven’t had any issues ourselves in the 6 weeks, I’ve seen lots of complaints of bands breaking. This wouldn’t be such an annoyance if the bands weren’t so pricey. Though I’ve friends that have bought &lt;a href="http://prodct.info/flex-bands"&gt;cheap replacement Flex bands&lt;/a&gt; and been happy with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;So, in summary; I would definitely recommend the &lt;a href="http://prodct.info/flex"&gt;Fitbit Flex&lt;/a&gt;. I would definitely &lt;em&gt;not&lt;/em&gt; recommend the &lt;a href="http://prodct.info/up24"&gt;Jawbone Up24&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That said; given the success of these sorts of bands, I think we’ll see an increase in the number of options for fitness bands over the coming 12 months, including better features (such as heart rate monitoring, GPS tracking of activities). With Apple and Google both building platforms for these devices to sit on top of (iOS Health app, Google Fit) I think we’ll also end up with a more consistent view of our data, regardless of what band we choose.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/08/jawbone-up24-vs-fitbit-flex/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/sX5NJ4zw7u4" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/sX5NJ4zw7u4/" title="Jawbone Up24 vs Fitbit Flex" />
	<feedburner:origLink>https://blog.dantup.com/2014/08/jawbone-up24-vs-fitbit-flex/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-06-29:/2014/06/why-i-returned-my-jawbone-up24-after-just-one-week/</id>
		<published>2014-06-29T00:00:00+00:00</published>
		<updated>2014-06-29T00:00:00+00:00</updated>
		
			<category term="Health" />
		
			<category term="Fitness Trackers" />
		
		<title type="text">Why I returned my Jawbone Up24 after just one week</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Last week I spent some time in and out of A&amp;amp;E with chest pains. It’s still not completely clear what the cause was; but the doctors seem to believe it’s not a problem with my heart. However, several ECG’s have shown an (intermittent!) unusual heart rhythm, so I’m currently awaiting for a 24hr ECG to understand what’s going on (maybe my heart rhythm is timed with IEEE floating point?).&lt;/p&gt;

&lt;p&gt;Whether this issue turns out to be related to my heart or not; it’s been a bit of a wake up call. I eat a lot of junk food, do no real exercise, sit at a computer all day every day, and barely eat 5 fruit&amp;amp;veg per month! I decided it’s time to start being a bit more health-conscious, and I hoped an activity tracker might motivate me a little more to get active.&lt;/p&gt;

&lt;p&gt;I did some comparing of the &lt;a href="http://prodct.info/flex"&gt;Fitbit Flex&lt;/a&gt; and the &lt;a href="http://prodct.info/up24"&gt;Jawbone Up24&lt;/a&gt; and decided to buy the Up24 for two features the Fitbit Flex didn’t have:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;“Intelligent” Alarm: Wakes you up within a defined window when you’re in a light sleep. Apparently being woken up from a deep sleep makes you feel worse.&lt;/li&gt;
  &lt;li&gt;Idle Alert: The wristband vibrates if you don’t move for a defined amount of time (eg. an hour). Since it’s easy to sit for a long time while programming, a reminder to get up and move seems like a good idea.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had the Up24 for almost a week, and I was generally impressed. The app was relatively polished (if a bit sluggish), and the push notifications at various intervals were quite handy. The food tracking worked fairly well, but had some annoying quirks (especially around managing your own “library”). However, there were two fairly serious flaws that ultimately resulted in me returning the device today.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The constant syncing of the band drains my Nexus 4 battery quickly. I used to get 2 days on a charge; but with the Up24 I was lucky to get 24 hours. Almost every day I woke up to a completely dead-and-turned-off phone, even sometimes if I’d charged it the night before.&lt;/li&gt;
  &lt;li&gt;Every time the band synced with my phone, it would disconnected all other Bluetooth devices. This means almost every phone call to my wife on the way home from work in my car got disconnected (the call stays active, in my pocket, until the sync finishes).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite not selling them in-store, Currys told me this was the third one they’d had back recently; so I’m guessing others haven’t been happy too.&lt;/p&gt;

&lt;p&gt;I don’t know whether either of these issues are Jawbone’s fault; it may turn out that the &lt;a href="http://prodct.info/flex"&gt;Fitbit Flex&lt;/a&gt; does the same (we’ll find out in the next few days!), though I did notice that on the Fitbit website it says that all-day-sync is disabled on my device by default due to battery drain issues. The difference here, is that the sync can be disabled. The &lt;a href="http://prodct.info/up24"&gt;Jawbone Up24&lt;/a&gt; doesn’t have any ability to disable the sync (or reduce the frequency).&lt;/p&gt;

&lt;p&gt;I do hope that these issues get ironed out (with software updates, or future versions of the bands/phones) and that Fitbit add the missing features I liked about the Up24. I think the idea of having a small piece of technology with us at all times to help us stay fit and healthy is a great one; and I think there’s huge scope for improvement in this area.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/06/why-i-returned-my-jawbone-up24-after-just-one-week/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/9xa7JWZgD6Y" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/9xa7JWZgD6Y/" title="Why I returned my Jawbone Up24 after just one week" />
	<feedburner:origLink>https://blog.dantup.com/2014/06/why-i-returned-my-jawbone-up24-after-just-one-week/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-06-03:/2014/06/dartvs-dart-support-for-visual-studio/</id>
		<published>2014-06-03T00:00:00+00:00</published>
		<updated>2014-06-03T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Dart" />
		
			<category term="Visual Studio" />
		
		<title type="text">DartVS - Dart Support for Visual Studio</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; DartVS is not being maintained, however &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code"&gt;Dart Code, my Dart extension for Visual Studio Code&lt;/a&gt; is and is much more complete.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yesterday I published my &lt;em&gt;fourth&lt;/em&gt; Visual Studio extension! :)&lt;/p&gt;

&lt;p&gt;Recently I’ve been playing around with Dart, in the hope it might one day be a viable alternative to JavaScript. Something that always put me off trying it out in the past was a lack of Visual Studio support (I don’t want to use another IDE, I’m already using VS for ASP.NET, etc.). Since it doesn’t look like Google nor Microsoft will ever add this, I decided to give it a shot myself - I mean, how hard can it be? ;-)&lt;/p&gt;

&lt;p&gt;Showing errors/warnings/hints in the Error List works, as does syntax highlighting (mostly). Next up is intellisense; but that’s likely to take a little longer as it’ll involve actually parsing some code!&lt;/p&gt;

&lt;p&gt;If you’re using Dart and have Visual Studio; please &lt;a href="http://visualstudiogallery.msdn.microsoft.com/69112f14-62d0-40fb-9ccc-03e3534e7121"&gt;try it out&lt;/a&gt; and let me know how you get on!&lt;/p&gt;

&lt;h2 id="implemented"&gt;Implemented&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Use DartAnalyzer from the SDK when saving any .dart file and report errors/warnings/hints to the Visual Studio error list&lt;/li&gt;
  &lt;li&gt;Clicking errors navigates to the correct place in code&lt;/li&gt;
  &lt;li&gt;Syntax Highlighting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="not-implemented"&gt;Not Implemented&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Use Batch Mode of DartAnalyzer for better performance&lt;/li&gt;
  &lt;li&gt;Intellisense&lt;/li&gt;
  &lt;li&gt;Debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Install from the &lt;a href="http://visualstudiogallery.msdn.microsoft.com/69112f14-62d0-40fb-9ccc-03e3534e7121"&gt;Visual Studio Gallery&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Download and unzip the &lt;a href="https://www.dartlang.org/tools/sdk/"&gt;Dart SDK&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Set the DART_SDK environment variable to point at the SDK root&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id="feedback"&gt;Feedback&lt;/h1&gt;
&lt;p&gt;Please send your feedback/issues/feature requests! :-)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub Issues: &lt;a href="https://github.com/DanTup/DartVS/issues"&gt;DartVS/issues&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Twitter: &lt;a href="https://twitter.com/DanTup"&gt;@DanTup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Email: &lt;a href="mailto:danny+dartvs@tuppeny.com"&gt;danny+dartvs@tuppeny.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/06/dartvs-dart-support-for-visual-studio/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/04-pIhJIyGY" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/04-pIhJIyGY/" title="DartVS - Dart Support for Visual Studio" />
	<feedburner:origLink>https://blog.dantup.com/2014/06/dartvs-dart-support-for-visual-studio/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-05-11:/2014/05/web-development-sucks-and-its-not-getting-any-better/</id>
		<published>2014-05-11T00:00:00+00:00</published>
		<updated>2014-05-11T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="ASP.NET" />
		
			<category term="Dart" />
		
			<category term="JavaScript" />
		
			<category term="TypeScript" />
		
		<title type="text">(Web) Development Sucks, and It's Not Getting Any Better</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;That’s quite a claim! I’m sure many will disagree with me, but since this is my blog you’re going to get my rant! :)&lt;/p&gt;

&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Over the past few months, I’ve been thinking more and more about the general frustrations around web programming in 2014. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There are more programming languages than there have ever been; yet it feels like there is no choice&lt;/li&gt;
  &lt;li&gt;We have more experience writing code than we ever have; yet we still struggle to express our requirements in code&lt;/li&gt;
  &lt;li&gt;We have higher level languages than we’ve ever had before; yet it feels like we’re writing more mundane boiler-plate plumbing&lt;/li&gt;
  &lt;li&gt;We have better browser support for standards; yet it’s more work than ever to support all of our users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It feels to me that as an industry, we’re treading water. Every week there’s something new and shiny, but little that progresses the significant things that could make an enormous difference to our jobs.&lt;/p&gt;

&lt;h3 id="no-choice-javascript-is-a-monopoly"&gt;No Choice; JavaScript is a Monopoly&lt;/h3&gt;

&lt;p&gt;It’s no secret that I’m not a big fan of JavaScript. It’s a hacked together mess with silly quirks and no typing. But the problem here is not my opinion of JavaScript, but one of choice…&lt;/p&gt;

&lt;p&gt;On the server, I can write my code in pretty much whatever programming language I want: C#, VB.NET, VBScript, Python, Perl, Erlang, Haskell, Clojure, Go, C… and the list goes on. Most of those even work across &lt;em&gt;different operating systems&lt;/em&gt;. This means I can pick the language/runtime that will give me the best productivity/stability/maintainability based on the task I’m doing. If I want quick prototyping, I can use something dynamic. If I’m writing something complex that is critical is correct, I can use something more formal.&lt;/p&gt;

&lt;p&gt;In the browser, I’m restricted to one language: JavaScript. This feels somewhat ironic given the open nature of the web! There’s no choice here. If I’m writing a huge app with millions of lines of code and I want something that can be safely type-checked and refactored, it’s tough. Your option is JavaScript. If you don’t like it, you can limit the client-side code you write, but that will be at the expense of user experience.&lt;/p&gt;

&lt;p&gt;Want to share code between client and server? Now you’re even more screwed, because you’re writing JavaScript on the server too ;)&lt;/p&gt;

&lt;p&gt;“AH, BUT…!” I hear you shout. And you’re right, there is another way! …&lt;/p&gt;

&lt;h3 id="tools-that-convert-to-javascript-are-a-hack"&gt;Tools That Convert to JavaScript are a HACK&lt;/h3&gt;

&lt;p&gt;Every month there’s a new tool that will let you write something that isn’t JavaScript and convert it to JavaScript. These range from simple ideas like TypeScript and new languages like Dart to huge programs to convert .NET and even native code. Well, they’re all a hack. Hack hack hackety hack. These tools are not being built because they are a good idea, they’re being built because we’re forced into using JavaScript and most people don’t like it. As a result, they’re usually full of edge cases, varying browser support, inconsistent performance and other strangeness.&lt;/p&gt;

&lt;h3 id="we-cannot-express-our-requirements-in-code"&gt;We Cannot Express our Requirements in Code&lt;/h3&gt;

&lt;p&gt;Even if you throw away all this client side nonsense for a minute, most mainstream programming languages still fail to allow us to express really common rules at compile time and force us to throw errors at runtime. There are lots of ideas out there about how we can fix it, but where’s the progress? Why is Microsoft’s Code Contracts still an unstable research project? Why hasn’t it had an update for 8 months? Why don’t more languages have dependent types? Why are there not more static analysis tools of this type? Why are even new programming languages being designed today with nullable references you can’t opt-out of?&lt;/p&gt;

&lt;h3 id="our-high-level-languages-are-not-high-level-enough"&gt;Our High-Level Languages are not High-Level Enough&lt;/h3&gt;

&lt;p&gt;With most languages today, it still feels like I’m writing a bunch of plumbing code that I shouldn’t need to. I can easily write a few hundred lines of code across several classes to do something, yet describe the functionality in just a few short sentences. Why do I still need so much third party code to perform basic functions? For example, look at &lt;a href="https://www.npmjs.org/package/exit"&gt;this nodejs module, exit&lt;/a&gt;. All it does is exit a node application in the way one would expect, after flushing standard streams like STDOUT. It’s a 41 line node module. A THIRD PARTY MODULE AND FORTY-ONE LINES TO EXIT CLEANLY!!&lt;/p&gt;

&lt;p&gt;How can we ever talk about declarative programming when we can’t even abstract the basics away?&lt;/p&gt;

&lt;h3 id="browserdevice-support-is-harder-than-ever"&gt;Browser/Device Support is Harder than Ever&lt;/h3&gt;

&lt;p&gt;Browsers implement standards better than they ever have; yet making your app work in all of your users browsers is more difficult today than ever before. Why? There are more browser rendering engines than there have ever been. Even within a single browser rendering engine, there are more versions out there today than there have ever been. IE having huge market share might have had its own issues, but was it really worse than battling with differences between IE7, IE8, IE9, IE10, IE11, IE Metro (likely multiple versions), Chrome, Firefox, Safari, Opera, Chrome for Android, Samsung’s Browser, etc.? And now let’s add in screen size and pixel density! People are trying to write media queries that use size to adjust to the screen, but it’s futile when small devices have hi-res screens and there are no clear boundaries. We need to base things on physical size, not pixel density, yet support for detecting this is poor. These things have become so complicated that services exist just to let you see/execute your apps across all of these devices because nobody can realistically have physical access to all of these things!&lt;/p&gt;

&lt;h2 id="how-can-we-improve-things"&gt;How can we Improve Things?&lt;/h2&gt;

&lt;p&gt;I think it all comes down to a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I should be able to pick my programming language&lt;/li&gt;
  &lt;li&gt;My programming language should be flexible enough for me to express things I know up-front and get compiler assistance instead of runtime errors&lt;/li&gt;
  &lt;li&gt;I should not have to write much code that is not &lt;em&gt;specific&lt;/em&gt; to my application/domain&lt;/li&gt;
  &lt;li&gt;My code should give the exact same results everywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stating requirements is easy; pulling them off less so! Note: I’m just an average developer! I’ve no experience with anything as complicated as what I’m proposing here. Don’t interpret this as me suggesting this is easy!&lt;/p&gt;

&lt;p&gt;Here’s what I think we should be investing time and effort into:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A new intermediate format/VM for running code in the browser to allow multiple languages
    &lt;ul&gt;
      &lt;li&gt;It doesn’t matter what this is based on; JVM, MSIL, NaCl, something else, as long as it’s something up to the job (with solid sensible behaviour, &lt;a href="https://www.destroyallsoftware.com/talks/wat"&gt;not JavaScript&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;It must support being targeted by at least a handful of mainstream languages (this will ensure suitability for new languages too)&lt;/li&gt;
      &lt;li&gt;Both Microsoft and Google agree to implement it (I’m certain other browser vendors would follow if both of these did, but I doubt that Google or MS would follow others)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;All browsers auto-update by default
    &lt;ul&gt;
      &lt;li&gt;No off-by-default, even for enterprise&lt;/li&gt;
      &lt;li&gt;Browser vendors drop support for old versions quickly&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Good static analysis tools for mainstream languages to allow us to move more runtime crashes to compile errors
    &lt;ul&gt;
      &lt;li&gt;Microsoft should get Code Contracts or something similar back on the roadmap&lt;/li&gt;
      &lt;li&gt;If we had a good intermediate format (as mentioned above), these tools may be able to be generic and work for multiple languages&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;New programming languages; It’d be very hard to try and retro-fit some of the things we need to existing languages
    &lt;ul&gt;
      &lt;li&gt;Designed to be runnable both on the server and within the browser (see above), including compile-time enforcement of API sets (eg. no access to file system in client libraries)&lt;/li&gt;
      &lt;li&gt;No nulls!&lt;/li&gt;
      &lt;li&gt;Contracts, Dependent Types or some other way to express our requirements better to cut down on runtime errors&lt;/li&gt;
      &lt;li&gt;Strong typing&lt;/li&gt;
      &lt;li&gt;Ability to enforce purity and exhaustiveness/totality on functions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may argue some of these are not &lt;em&gt;necessary&lt;/em&gt;, but I think the end goal needs to include all of these, even if the path to get there is a progressive one.&lt;/p&gt;

&lt;h3 id="the-chances-of-things-getting-better-are-slim"&gt;The Chances of Things Getting Better are Slim&lt;/h3&gt;

&lt;p&gt;That’s a pretty pessimistic view, but sadly I think it’s where we are. The problem is; the entire “plan” above pretty much hinges on support and collaboration between two of the biggest software companies in the world: Microsoft and Google. Both of these companies have a serious chunk of the browser market and it’s a near-impossible task to ever challenge that. With their rocky relationship I don’t see a world where either company will add the others technology to their browser (eg. IE getting NaCl or even Dart support), nor anyone coming up with a standard intermediate format they would both agree to support. Browser plugins are not a realistic option even today and their support is only likely to shrink in the future.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you’re writing code on the server, you’ve got it great. You can pick and choose your language and over time, you’ll get more of the programming language features you need, or new languages to provide them.&lt;/p&gt;

&lt;p&gt;If you’re writing code for the browser, then unless you love JavaScript, your life is going to continue to be frustrating for many many years to come!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/05/web-development-sucks-and-its-not-getting-any-better/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/9m4a71I0jjQ" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/9m4a71I0jjQ/" title="(Web) Development Sucks, and It's Not Getting Any Better" />
	<feedburner:origLink>https://blog.dantup.com/2014/05/web-development-sucks-and-its-not-getting-any-better/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-04-19:/2014/04/is-fsharp-ready-for-production/</id>
		<published>2014-04-19T00:00:00+00:00</published>
		<updated>2014-04-19T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="F#" />
		
		<title type="text">Is F# Ready for Production?</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;It took me some time to get my head around F#. I had no experience with functional programming and failed twice to pick it up. I persevered; and third time lucky, it started to make sense!&lt;/p&gt;

&lt;p&gt;I became a huge fan! Being part of the .NET Framework made it feel fairly familiar and more likely we could start to introduce it at work (we’re primarily .NET devs). Options instead of nulls; immutability, pattern matching, DUs, loads of stuff that looked like it would solve problems we have every day (especially the nulls - we have a &amp;gt; 200k LoC inherited legacy codebase that started life in .NET 1.1 to fight with).&lt;/p&gt;

&lt;p&gt;I gave a talk to colleagues who were (inexplicably!) unconvinced, so I gave another one a little later and soon after one of my colleagues arranged working through &lt;a href="https://github.com/ChrisMarinos/FSharpKoans"&gt;F# Koans&lt;/a&gt; as a team; and the wheels seemed to be moving.&lt;/p&gt;

&lt;p&gt;Since we’re busy as ever; F# sort of fell to one side. We were all busy working meeting deadlines; and as a team we weren’t confident enough in F# to pull it into production. The team were even less convinced about F# after I declared PowerShell to be an annoying crock of &lt;a href="http://connect.microsoft.com/PowerShell/feedback/details/778371/invoke-webrequest-getelementsbytagname-is-incredibly-slow-on-some-machines"&gt;buggy&lt;/a&gt; &lt;a href="http://connect.microsoft.com/PowerShell/feedback/details/776801/weekly-tasks-created-via-powershell-using-a-different-user-immediately-fail-with-error-0x41306"&gt;shit&lt;/a&gt; that gets &lt;a href="https://twitter.com/jsnover/status/419214625691795456"&gt;no responses to bug reports&lt;/a&gt;, when I’d been so enthusiastic about it months earlier. They expected I’d be announcing F# to be a big failure soon too (oh, the irony of this blog post!) :-/&lt;/p&gt;

&lt;p&gt;Fast forward a few months; and I’m charging along again. I got the latest F# up and running on the work build server. Production are up to .NET 4.5.1. I set up an internal NuGet feed and extracted some simple libraries from our main products solution into NuGet packages for easy replacement. Everything is ready to start getting small F# libraries into the main product, allowing the team to get more familiar with writing and reviewing F#! :)&lt;/p&gt;

&lt;h2 id="so-whats-the-problem-get-to-it-already"&gt;So, what’s the problem? Get to it already!&lt;/h2&gt;

&lt;p&gt;Well; once we take the dive; it’s hard to go back. This means I’ve been thinking more about it this week than previously; and I’m now starting to wonder if F# is really ready. There are a few little things that quite bug me…&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;IDE support even in Visual Studio 2013 is absolutely poor. Basic things like document formatting and refactoring are missing. The community have worked hard on improving this somewhat; but I hit &lt;a href="https://github.com/fsprojects/VisualFSharpPowerTools/issues/352"&gt;serious bugs&lt;/a&gt; (&lt;em&gt;edit: this was fixed within 2 days; kudos!&lt;/em&gt;) in their extension within minutes. We spend a &lt;em&gt;lot&lt;/em&gt; of time in our IDEs; we need first class support; and F# is miles off.&lt;/li&gt;
  &lt;li&gt;Options are great; nulls are bad. But because F# runs on the CLR; nulls are not gone. This means we now just have two different types of null; the bad one, and the good one. I’m on the fence about whether this is really an improvement. Even if you have the luxury of being F# for &lt;em&gt;all&lt;/em&gt; of your own code; the BCL is riddled with null returning/expecting methods.&lt;/li&gt;
  &lt;li&gt;F# was completely missed from Roslyn. I understand the reasons for this; but with this new “open” Microsoft; there’s still no word on whether this will happen. If it doesn’t; it means F# will miss out on a new generation of tools being built on Roslyn (for example, Code Diagnostics/Fix support).&lt;/li&gt;
  &lt;li&gt;There’s little info on what’s coming in the next version of F#; yet we know quite a lot of language features coming in the new versions of VB and C#. It feels like C# and VB are charging forwards at a much faster rate than F#. Do we want to get off the fast train and board the slow train?&lt;/li&gt;
  &lt;li&gt;Hiring developers is already very hard. Do we want to limit the pool of available candidates significantly (and take the increased salaries that come with the harder-to-find skills)?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scott Hanselman was right; &lt;a href="https://twitter.com/shanselman/status/457444398268030977"&gt;programming is hard&lt;/a&gt;, but apparently not just the writing of code!&lt;/p&gt;

&lt;p&gt;I’m sure use of F# will continue to grow at our company. Both myself and the other team leader are both big fans. However I think the journey will be &lt;em&gt;much&lt;/em&gt; slower and more cautious than I’d originally hoped.&lt;/p&gt;

&lt;p&gt;Don’t take this article as a bashing of F#; I’m still a huge fan (both of F# and general FP ideas), but I think it may be some time before it’s the &lt;em&gt;obvious&lt;/em&gt; choice to make.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/04/is-fsharp-ready-for-production/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/mfhvZ62AvHQ" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/mfhvZ62AvHQ/" title="Is F# Ready for Production?" />
	<feedburner:origLink>https://blog.dantup.com/2014/04/is-fsharp-ready-for-production/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-04-15:/2014/04/free-software-and-services-for-open-source-projects/</id>
		<published>2014-04-15T00:00:00+00:00</published>
		<updated>2014-04-15T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="AppHarbor" />
		
			<category term="Azure" />
		
			<category term="BitBucket" />
		
			<category term="Git" />
		
			<category term="Google Code" />
		
			<category term="Kiln" />
		
			<category term="Testing" />
		
			<category term="Codeplex" />
		
			<category term="GitHub" />
		
			<category term="Mercurial" />
		
		<title type="text">Free Software and Services for Open Source Projects</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Yesterday &lt;a href="/2014/04/my-favourite-open-source-software/"&gt;I blogged a list of some of my favourite open source projects&lt;/a&gt;. I thought it was also worth blogging some of my favourite services/software with free offerings for open source projects that make it much easier or cheaper for open source projects to exist, collaborate and build great software.&lt;/p&gt;

&lt;p&gt;I’ve tried to limit the list to things I have first-hand experience of or seem popular amongst people I follow on social networks; but there’s a chance there are omissions. &lt;a href="https://twitter.com/dantup"&gt;Ping me on Twitter&lt;/a&gt; if you think there’s something I should really add.&lt;/p&gt;

&lt;h2 id="source-code-hosting"&gt;Source Code Hosting&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; - Git source code hosting; issue tracker and web page hosting. By far the most popular/active for open source projects right now.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.codeplex.com/"&gt;Codeplex&lt;/a&gt; - Git, TFS and Mercurial source code hosting, issue tracker, wiki. Needs a bit of love, to be honest!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://bitbucket.org/"&gt;BitBucket&lt;/a&gt; - Git and Mercurial source code hosting. BitBucket has free private repos too (so isn’t just free for open source). I used to use this quite a lot until GitHub got so popular that it was hard to ignore!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://code.google.com/"&gt;Google Code&lt;/a&gt; - Git, Mercurial and Subversion. Doesn’t seem to be much here these days; mostly just dead projects. Even lots of Google’s own OSS is hosted on GitHub!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.fogcreek.com/kiln/pricing/"&gt;Kiln&lt;/a&gt;/&lt;a href="https://www.fogcreek.com/fogbugz/pricing/ondemand.html"&gt;FogBugz&lt;/a&gt; - Whilst this isn’t strictly for open source; Kiln and FogBugz are free for two users and supports “community users” for public access. I couldn’t not include this; it’s my favourite DVCS hosting and includes a great Code Review system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="web-app-hosting"&gt;Web App Hosting&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://azure.microsoft.com/en-gb/develop/net/aspnet/"&gt;Azure&lt;/a&gt; - Host 10 web apps for free; though you’ll need to pay for custom domains.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://appharbor.com/page/community-application-program"&gt;AppHarbor&lt;/a&gt; - A .NET PaaS (Platform as a Service) provider. When first launched, this service made Azure look incredibly clunky and complicated for simple web apps, and used to host the website for &lt;a href="http://gplusnotifier.dantup.com/"&gt;G+ Notifier&lt;/a&gt; for free under their OSS program.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="hosted-continuous-integration--deployment"&gt;Hosted Continuous Integration / Deployment&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://travis-ci.com/"&gt;Travis CI&lt;/a&gt; - This seems to be getting more and more popular for GitHub projects, possibly down to really nice integration that allows running tests and reporting pass/fail status in pull requests &lt;em&gt;automatically&lt;/em&gt;!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://buildhive.cloudbees.com/"&gt;BuildHive&lt;/a&gt; - Powered by Jenkins, also with easy integration with GitHub.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://codebetter.com/codebetter-ci/"&gt;CodeBetter CI&lt;/a&gt; - Powered by Team City.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.atlassian.com/opensource/overview"&gt;Bamboo&lt;/a&gt; - From Atlassian; also available for download.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://saucelabs.com/opensauce"&gt;Sauce Labs&lt;/a&gt; - Run your web and mobile app tests across hundreds of real browsers and platforms.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.browserstack.com/javascript-testing-api"&gt;BrowserStack JavaScript Testing&lt;/a&gt; - An HTTP-based API that can be used to open any URL in any combination of browser/OS on BrowserStack to execute JavaScript tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="other-development-software--web-apps"&gt;Other Development Software / Web Apps&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://www.jetbrains.com/teamcity/buy/choose_edition.jsp?license=OPEN_SOURCE"&gt;TeamCity&lt;/a&gt; - Continuous integration server (also available hosted as CodeBetter CI).&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://reflectorblog.red-gate.com/2013/07/open-source/"&gt;Reflector / ANTS Profilers&lt;/a&gt; - Code and memory profilers and decompiler for .NET.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://community.moqups.com/knowledgebase/articles/188147-faq"&gt;Moqups&lt;/a&gt; - Online tool for mocking up UIs quickly.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://balsamiq.com/free/"&gt;myBalsamiq Mockups&lt;/a&gt; - Online tool for mocking up UIs quickly.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.atlassian.com/opensource/overview"&gt;JIRA / Bamboo / Confluence&lt;/a&gt; - Issue tracker, continious integration and team collaboration software. Also available hosted.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://rhodecode.com/pricing"&gt;RhodeCode&lt;/a&gt; - A locally-installed DVCS and code review system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="misc"&gt;Misc&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://uk.godaddy.com/ssl/ssl-open-source.aspx"&gt;GoDaddy SSL Certificates&lt;/a&gt; - I’m not sure there’s much for love for these guys, but they are claiming to give free SSL certs for OSS.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://crowdin.net/page/open-source-project-setup-request"&gt;Crowdin&lt;/a&gt; - Localisation platform to help manage your translations.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.bugsense.com/"&gt;BugSense&lt;/a&gt; - Analytics software for catching and collating errors in mobile apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After posting this; I was tweeted a link to &lt;a href="http://ossperks.com/"&gt;ossperks.com&lt;/a&gt;, a similar (but more-complete) list of free stuff for Open Source projects. If you’re running an OSS project, Irecommend checking it out!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/04/free-software-and-services-for-open-source-projects/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/GZsMXn_A6Po" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/GZsMXn_A6Po/" title="Free Software and Services for Open Source Projects" />
	<feedburner:origLink>https://blog.dantup.com/2014/04/free-software-and-services-for-open-source-projects/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-04-14:/2014/04/my-favourite-open-source-software/</id>
		<published>2014-04-14T00:00:00+00:00</published>
		<updated>2014-04-14T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="JavaScript" />
		
			<category term=".NET" />
		
			<category term="F#" />
		
			<category term="Testing" />
		
			<category term="ASP.NET" />
		
			<category term="Jasmine" />
		
			<category term="Karma" />
		
		<title type="text">My Favourite Open Source Software</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;(You may also be interested in my post &lt;a href="/2014/04/free-software-and-services-for-open-source-projects/"&gt;Free Software and Services for Open Source Projects&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;In a &lt;a href="http://www.hanselman.com/blog/OpenSourceIsAThanklessJobWeDoItAnyway.aspx"&gt;recent blog post&lt;/a&gt;, Scott Hanselman suggested:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We don’t sing contributor’s praises for their hard work and success while their software works, instead we wait until a single line (albeit one of the more important lines) fails to live up to expectations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don’t completely agree with this; I often see people praising open source software (and I didn’t see anyone really pointing fingers over Heartbleed), but I do think the general point that open source software is taken for granted and under-appreciated is valid.&lt;/p&gt;

&lt;p&gt;This got me thinking about some of the open source software I use; some as part of my job delivering commercial applications and some at home when I’m just playing around. I thought it’d be good to blog a list of my favourites; maybe it’ll help others discover them. Much of it is pretty well known (some even from MS/Google), but I thought I’d include all my favourite stuff for completeness.&lt;/p&gt;

&lt;p&gt;I may have missed some; but there’s a lot of great stuff out there. If I remember anything cool Ive missed, I’ll try and add it in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have a blog (or even Google+ ;-)) I invite you to do the same!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(In alphabetical order; with no grouping, and often their own marketing speak!)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://www.7-zip.org/"&gt;7-Zip&lt;/a&gt; - A file archiver with a high compression ratio. There’s a portable version I use to avoid having extra stuff installed, but still allowing the higher compression of .7z when required.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://angularjs.org/"&gt;AngularJS&lt;/a&gt; - A toolset for building the framework most suited to your application development. I’m currently playing around with this prototyping some ideas for a possible vNext of a big project at work; I think the future of web apps is (sadly ;)) in client-side JavaScript with API backends.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; - A popular front-end framework for developing responsive, mobile first projects on the web. This is great for getting something looking decent very quickly, especially for non-designers (programmers!).&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://harvesthq.github.io/chosen/"&gt;Chosen&lt;/a&gt; - A jQuery plugin that makes long, unwieldy select boxes much more user-friendly. These look really nice; especially compared against the default selects in some of the latest browser versions!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://code.google.com/p/elmah/"&gt;ELMAH&lt;/a&gt; - An application-wide error logging facility.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://fsharp.org/"&gt;F#&lt;/a&gt; - A functional-first programming language that interops relatively nicely with .NET. This could be the key to getting current-.NET devs interested more in functional ideas.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/fsharp/FAKE"&gt;FAKE&lt;/a&gt; - A cross platform build automation system (F# Make). Haven’t used this seriously yet; but looking for someone to replace all the built steps on our build server with a single script that can be run on dev machines to give a like-for-like build.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://getglimpse.com/"&gt;Glimpse&lt;/a&gt; - Inspects web requests as they happen, providing insights and tooling that reduce debugging time.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://jasmine.github.io/"&gt;Jasmine&lt;/a&gt; - A nice behavior-driven development framework for testing JavaScript code.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; - A blog-aware, static-site generator (powering this blog for free, via GitHub Pages!)&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://karma-runner.github.io/"&gt;Karma&lt;/a&gt; - JavaScript test runner that executes tests in multiple browsers and gives instant feedback (now with &lt;a href="/2014/03/cross-browser-javascript-testing-with-karma-and-visual-studio/"&gt;Visual Studio integration ;)&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://knockoutjs.com/"&gt;Knockout&lt;/a&gt; - A JavaScript library that helps create rich, responsive display and editor user interfaces using two-way data-binding. This helps de-couple JavaScript from the DOM, making things &lt;em&gt;much&lt;/em&gt; simpler!&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://mercurial.selenic.com/"&gt;Mercurial&lt;/a&gt; - A distributed source control management tool. This is &lt;em&gt;much&lt;/em&gt; easier IMO to use than Git; though sadly I don’t think I can convicne the masses ;)&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt; - A platform built on Chrome’s V8 JavaScript runtime for building fast, scalable network applications. Great package manager, and really useful for non-browser based JS execution.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.nuget.org/"&gt;NuGet&lt;/a&gt; - A package manager for the Microsoft development platform including .NET. This is basically the first place I look for &lt;em&gt;any&lt;/em&gt; third party library these days.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://rx.codeplex.com/"&gt;Reactive Extensions&lt;/a&gt; - A library for composing async/event-based programs using observable sequences and LINQ-style query operators.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://remarkjs.com"&gt;remark&lt;/a&gt; - A simple, in-browser, Markdown-driven slideshow tool targeted at people who know their way around HTML and CSS (&lt;a href="/2014/04/markdown-based-presentations-with-remark/"&gt;I blogged more about this yesterday&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://docs.seleniumhq.org/"&gt;Selenium&lt;/a&gt; - Automates browsers to aid testing of web applications. If we’re going to start writing more client-side JavaScript for our web apps; we &lt;em&gt;need&lt;/em&gt; to get better at testing them!&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/SignalR/SignalR"&gt;SignalR&lt;/a&gt; -  A library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to applications.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://docs.structuremap.net/"&gt;StructureMap&lt;/a&gt; - A dependency injection / inversion of control tool for .NET.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://tickspec.codeplex.com/"&gt;TickSpec&lt;/a&gt; - A lightweight F# BDD framework.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; - A typed superset of JavaScript that compiles to plain JavaScript.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://aspnetwebstack.codeplex.com/"&gt;Web API&lt;/a&gt; - A framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/xunit/xunit"&gt;xUnit&lt;/a&gt; - A community-focused unit testing tool for the .NET Framework.&lt;/li&gt;
&lt;/ul&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/04/my-favourite-open-source-software/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/xQq9DDd7F6s" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/xQq9DDd7F6s/" title="My Favourite Open Source Software" />
	<feedburner:origLink>https://blog.dantup.com/2014/04/my-favourite-open-source-software/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-04-13:/2014/04/markdown-based-presentations-with-remark/</id>
		<published>2014-04-13T00:00:00+00:00</published>
		<updated>2014-04-13T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="JavaScript" />
		
		<title type="text">Markdown-based Presentations with Remark</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;After giving a talk at work to colleagues recently using slides I’d created in Google Docs; I thought I’d had a great idea! What if I could edit my slides as text (something like Markdown) and bypass the nasty WYSIWYG editor?!&lt;/p&gt;

&lt;p&gt;Well; as is usually the case when you have a great idea; it’s already been done. I played around with a few I found online and one that was &lt;em&gt;really&lt;/em&gt; neat. Not only does it support markdown; but it also has extensions so you can add CSS classes to content to lay things out just like a web developer is used to! As it’s web-based, you can even put your own JavaScript in to mess with things further!&lt;/p&gt;

&lt;p&gt;The project is called &lt;a href="http://remarkjs.com/"&gt;remark&lt;/a&gt;, you can see a slideshow created using their script on &lt;a href="http://remarkjs.com/"&gt;their website&lt;/a&gt;. The source text can be &lt;a href="https://github.com/gnab/remark/blob/gh-pages/index.html#L123"&gt;seen here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to being able to knock together a slideshow in text; there are some other cool features (you can try these out on &lt;a href="http://remarkjs.com/"&gt;their website slideshow&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Press ‘c’ to clone the window, so you can show a separate copy on an external screen (the clones are kept in sync with an API)&lt;/li&gt;
  &lt;li&gt;Press ‘p’ to open presenter mode for the active window, which shows current/next slide and speaker notes (and unlike Google Docs; the slide preview is &lt;em&gt;not tiny&lt;/em&gt;!)&lt;/li&gt;
  &lt;li&gt;Press ‘f’ to make the slideshow full screen (you can set the aspect ratio in the code)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I often use other peoples software; but I don’t have enough time to contribute to many. However, I liked this one so much, I had a stab at fixing a niggle I found; and it’s &lt;a href="https://github.com/gnab/remark/pull/106"&gt;now been merged into the main repo&lt;/a&gt; :)&lt;/p&gt;

&lt;h1 id="slide"&gt;Slide&lt;/h1&gt;
&lt;p&gt;&lt;img src="/post_images/remark_slide_thumb.png" alt="remark slide" /&gt;&lt;/p&gt;

&lt;h1 id="presenter-mode"&gt;Presenter Mode&lt;/h1&gt;
&lt;p&gt;&lt;img src="/post_images/remark_presenter_thumb.png" alt="remark presenter mode" /&gt;&lt;/p&gt;

&lt;p&gt;So, it all looks pretty slick. One thing I’m certain of; if this hadn’t existed and I’d started my own; it wouldn’t have looked this neat! If you’re a web developer; give it a go. I’m certain you’ll prefer it to PowerPoint and Google Docs! :)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/04/markdown-based-presentations-with-remark/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/FudxgnMsLgY" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/FudxgnMsLgY/" title="Markdown-based Presentations with Remark" />
	<feedburner:origLink>https://blog.dantup.com/2014/04/markdown-based-presentations-with-remark/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-03-09:/2014/03/cross-browser-javascript-testing-with-karma-and-visual-studio/</id>
		<published>2014-03-09T00:00:00+00:00</published>
		<updated>2014-03-09T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="JavaScript" />
		
			<category term="Jasmine" />
		
			<category term="Karma" />
		
			<category term="Testing" />
		
			<category term="Visual Studio" />
		
		<title type="text">Cross-browser JavaScript Testing with Karma and Visual Studio</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/TestAdapters"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yesterday I published my &lt;em&gt;third&lt;/em&gt; Visual Studio extension! :)&lt;/p&gt;

&lt;p&gt;It’s a fairly generic extension, with reads static XML files that contain test results into the Visual Studio Test Explorer window. While this might sound a bit strange, its goal is to integrate with third party tools that can output results as XML and update them realtime. For example, Google’s Karma test runner.&lt;/p&gt;

&lt;p&gt;Here’s a video showing it all in action:&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/-28j6Ek8D7w?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;h1 id="installation-and-setup"&gt;Installation and Setup&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;Install my Karma Test Adapter from &lt;a href="http://visualstudiogallery.msdn.microsoft.com/bfe6feb7-7ec4-4e8e-9d90-cf6ea2cd2169"&gt;Visual Studio Gallery&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Install/download nodejs (I grabbed the exe &lt;a href="http://nodejs.org/dist/"&gt;from here&lt;/a&gt; and added it to my PATH)&lt;/li&gt;
  &lt;li&gt;Install/download npm (I grabbed the zip &lt;a href="http://nodejs.org/dist/npm/"&gt;from here&lt;/a&gt; and put it in the same folder as node.exe)&lt;/li&gt;
  &lt;li&gt;Install the karma-cli node module globally
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install -g karma-cli&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id="usage"&gt;Usage&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;Create/navigate to the folder that contains/will contain your tests&lt;/li&gt;
  &lt;li&gt;Create a package.json to hold your node dependencies
    &lt;ul&gt;
      &lt;li&gt;PowerShell: &lt;code class="highlighter-rouge"&gt;"{}" | Out-File -Encoding ascii package.json&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;or CMD: &lt;code class="highlighter-rouge"&gt;echo '{}' &amp;gt; package.json&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Install karma and some dependencies
    &lt;ol&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma --save-dev&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-xml-reporter --save-dev&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-jasmine --save-dev&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-phantomjs-launcher --save-dev&lt;/code&gt; &lt;em&gt;(required only if you want to use PhantomJS)&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-ie-launcher --save-dev&lt;/code&gt; &lt;em&gt;(required only if you want Karma to be able to launch IE)&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-chrome-launcher --save-dev&lt;/code&gt; &lt;em&gt;(required only if you want Karma to be able to launch Chrome)&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;npm install karma-firefox-launcher --save-dev&lt;/code&gt; &lt;em&gt;(required only if you want Karma to be able to launch Firefox)&lt;/em&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Create a karma config file in the folder with your tests
    &lt;ol&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;karma init&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;answer the questions, for example:&lt;/em&gt;
        &lt;ol&gt;
          &lt;li&gt;Test framework: jasmine&lt;/li&gt;
          &lt;li&gt;RequireJS: no&lt;/li&gt;
          &lt;li&gt;Browsers:
            &lt;ul&gt;
              &lt;li&gt;Chrome&lt;/li&gt;
              &lt;li&gt;IE&lt;/li&gt;
              &lt;li&gt;PhantomJS&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;JavaScript and Test files:
            &lt;ul&gt;
              &lt;li&gt;app\**.js&lt;/li&gt;
              &lt;li&gt;tests\**.js&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;Exclusions: &lt;em&gt;(none)&lt;/em&gt;&lt;/li&gt;
          &lt;li&gt;Watch for changes: yes&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Open up the generated karma.config.js in your text editor and add ‘xml’ as a reporter
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;reporters: ['progress']&lt;/code&gt; becomes &lt;code class="highlighter-rouge"&gt;reporters: ['progress', 'xml']&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Open Visual Studio and create a project that lives in the folder that contains your tests/karma config file
    &lt;ul&gt;
      &lt;li&gt;I usually choose File -&amp;gt; Open Web Site and choose the FileSystem option, pointing at the folder&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Create a simple test we can use to verify everything works. The filename must match the filter you gave to &lt;code class="highlighter-rouge"&gt;karma init&lt;/code&gt; earlier (eg. &lt;code class="highlighter-rouge"&gt;MyTestSpecs.js&lt;/code&gt;)
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;describe("a test", function() {
       it("passes", function() {
           expect(true).toBe(true);
       });
   });&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;From the command line, start karma
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="highlighter-rouge"&gt;karma start&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;note: This will fire up the browsers that you configured; these must be left open to execute tests&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In Visual Studio, toggle the “Run Tests on Build” option &lt;em&gt;on&lt;/em&gt; in the Test Explorer Window&lt;/li&gt;
  &lt;li&gt;Right-click -&amp;gt; Refresh on the project, to ensure Visual Studio spots the test-results.testxml file&lt;/li&gt;
  &lt;li&gt;As you make changes to your js files and press save, Karma will now automatically execute your tests and the results will magically update in Visual Studio!&lt;/li&gt;
  &lt;li&gt;Bonus: You can open up other browsers and point them at the url that’s loaded in the existing browsers and they will automatically be used for tests. Eg. enter the URL into your smartphone browser, and now it will show up in the test results too!&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id="feedback"&gt;Feedback&lt;/h1&gt;
&lt;p&gt;Please send your feedback/issues/feature requests! :-)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub Issues: &lt;a href="https://github.com/DanTup/TestAdapters/issues"&gt;TestAdapters/issues&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Twitter: &lt;a href="https://twitter.com/DanTup"&gt;@DanTup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Email: &lt;a href="mailto:danny+testadapters@tuppeny.com"&gt;danny+testadapters@tuppeny.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/03/cross-browser-javascript-testing-with-karma-and-visual-studio/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/76wUKfpojRA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/76wUKfpojRA/" title="Cross-browser JavaScript Testing with Karma and Visual Studio" />
	<feedburner:origLink>https://blog.dantup.com/2014/03/cross-browser-javascript-testing-with-karma-and-visual-studio/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-03-02:/2014/03/a-jasmine-test-adapter-for-visual-studio/</id>
		<published>2014-03-02T00:00:00+00:00</published>
		<updated>2014-03-02T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="JavaScript" />
		
			<category term="Jasmine" />
		
			<category term="Visual Studio" />
		
			<category term="Testing" />
		
		<title type="text">A Jasmine Test Adapter for Visual Studio</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/TestAdapters"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today I published my &lt;em&gt;second&lt;/em&gt; Visual Studio extension! :)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://visualstudiogallery.msdn.microsoft.com/102979e0-61ba-4c6f-a18c-ca64cc7bd2c6"&gt;Jasmine Test Adapter for Visual Studio&lt;/a&gt;&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/Gc4xLjUxxOY?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;It’s similar to the &lt;a href="/2014/02/a-lua-test-adapter-and-framework-for-visual-studio/"&gt;Lua Adapter&lt;/a&gt; I created recently, but uses node.exe instead of lua.exe and includes an internal copy of the Jasmine JavaScript testing framework. I’ve started playing around with AngularJS, and since it seems to be written with unit testing in mind, I somehow ended up at Jasmine and decided it needed an easy way to be run within Visual Studio!&lt;/p&gt;

&lt;p&gt;&lt;img src="https://github.com/DanTup/TestAdapters/raw/master/DanTup.TestAdapters.Jasmine.Vsix/Screenshot.png" alt="Screenshot of Jasmine Test Adapter in action" /&gt;&lt;/p&gt;

&lt;p&gt;If you try it out, please do send feedback!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/03/a-jasmine-test-adapter-for-visual-studio/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/iymQifAoN9c" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/iymQifAoN9c/" title="A Jasmine Test Adapter for Visual Studio" />
	<feedburner:origLink>https://blog.dantup.com/2014/03/a-jasmine-test-adapter-for-visual-studio/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-02-16:/2014/02/some-things-i-learned-while-building-my-visual-studio-test-adapter/</id>
		<published>2014-02-16T00:00:00+00:00</published>
		<updated>2014-02-16T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Lua" />
		
			<category term="Visual Studio" />
		
			<category term="Testing" />
		
		<title type="text">Some things I learned while building my Visual Studio Test Adapter</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Here’s a couple of things I learned while creating a Visual Studio test adapter that included a custom ITestContainerDiscoverer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is almost no documentation on creating Visual Studio test adapters (and even less on ITestContainerDiscoverer and friends)&lt;/li&gt;
  &lt;li&gt;You need to install the Visual Sudio SDK to get the required assemblies for creating test container discoverers (but not for just test adapters)&lt;/li&gt;
  &lt;li&gt;You cannot create a test adapter without a test container discoverer if you want to scan non-DLL/EXE files&lt;/li&gt;
  &lt;li&gt;Although you can deploy test adapters via NuGet; this does not work for test container discoverers; they must be registered as real Visual Studio extensions&lt;/li&gt;
  &lt;li&gt;To have Visual Studio load your test container discoverer, you must add an asset with type “UnitTestExtension” to your VSIX; an option which is not in the list (nor, seemingly, documented)&lt;/li&gt;
  &lt;li&gt;Just because you’re in the middle of responding to a request from Visual Studio to get a list of tests; doesn’t mean it won’t ask you AGAIN AT THE SAME TIME on another thread :/&lt;/li&gt;
  &lt;li&gt;When building Visual Studio extensions, the entire VSIX output folder is copied into the expirimental instance, not just the files you’d get via the VSIX. This means references to other projects might work fine on your machine, but not on somebody elses!&lt;/li&gt;
  &lt;li&gt;The “Run tests after build” function actually means “when test container change”, so even for non-building projects (eg. lua tests), it can work when you save files :)&lt;/li&gt;
&lt;/ul&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/02/some-things-i-learned-while-building-my-visual-studio-test-adapter/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/i4viIHVf0ZM" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/i4viIHVf0ZM/" title="Some things I learned while building my Visual Studio Test Adapter" />
	<feedburner:origLink>https://blog.dantup.com/2014/02/some-things-i-learned-while-building-my-visual-studio-test-adapter/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2014-02-16:/2014/02/a-lua-test-adapter-and-framework-for-visual-studio/</id>
		<published>2014-02-16T00:00:00+00:00</published>
		<updated>2014-02-16T00:00:00+00:00</updated>
		
			<category term="Open Source" />
		
			<category term="Lua" />
		
			<category term="Visual Studio" />
		
			<category term="Testing" />
		
		<title type="text">A Lua Test Adapter and Framework for Visual Studio</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;&lt;a href="https://github.com/DanTup/TestAdapters"&gt;
	&lt;img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today I published my first Visual Studio extension! :)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://visualstudiogallery.msdn.microsoft.com/8a046271-217f-48b6-8293-2b8447081695"&gt;Lua Test Adapter and Framework&lt;/a&gt;&lt;/p&gt;

&lt;iframe width="450" height="253" src="https://www.youtube.com/embed/fW3L3LTMdBw?VQ=HD720" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;It was created because I’d recently been &lt;a href="https://github.com/DanTup/RareShare"&gt;working on a World of Warcraft Addon, RareShare&lt;/a&gt;. As the addon currently has no UI (mostly just interacts with the World of Warcraft API), I decided to create a set of automated tests for it (as any self-respecting software developer would ;)). I quickly became annoyed by reading the output of lua.exe and then looking up test line numbers, so decided to investigate how hard it was to create a Visual Studio test adapter!&lt;/p&gt;

&lt;p&gt;It actually turned out to be slightly more complicated than expected; so I’ll leave that for the next post. However, I did build it in a way that keeps the Lua-specific stuff out of the main assembly; such that it should be quite simple to add other types of external tests (eg. Python, Perl, JavaScript, whatever). I can’t promise I’ll get around to any of them; but I might take pull requests ;-)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2014/02/a-lua-test-adapter-and-framework-for-visual-studio/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/FTCQDiyIjHo" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/FTCQDiyIjHo/" title="A Lua Test Adapter and Framework for Visual Studio" />
	<feedburner:origLink>https://blog.dantup.com/2014/02/a-lua-test-adapter-and-framework-for-visual-studio/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-10-26:/2013/10/easily-calling-windows-apis-from-powershell/</id>
		<published>2013-10-26T00:00:00+00:00</published>
		<updated>2013-10-26T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="PowerShell" />
		
		<title type="text">Easily Calling Windows APIs from PowerShell</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;After my &lt;a href="/2013/10/useful-powershell-profile-snippets/"&gt;previous post of some snippets from my PowerShell profile&lt;/a&gt;; I received another email from the person that prompted me to write that post, asking some questions about calling Windows APIs from PowerShell. It turns out that this isn’t so straight-forward, despite PowerShell’s pitch as a system automation language!&lt;/p&gt;

&lt;p&gt;The confusion came from various varying samples online of how to do this in PowerShell; and they all used blocks of C#, which many PowerShell users aren’t particularly familiar with.&lt;/p&gt;

&lt;p&gt;Although I couldn’t find a way to do this without the C# wrapper, I did think it was worthwhile extracting some parts out of the C# code to avoid having to manipulate so much C# code in a quoted string inside PowerShell to add new Windows API methods. Unfortunately the argument types still need to be in C#, because I don’t know how to map them from the C++ examples easily.&lt;/p&gt;

&lt;p&gt;It’s very basic; and can probably be improved by anyone that has some knowledge of calling Windows APIs; but hopefully it’s a little simpler if you’re less comfortable with C#.&lt;/p&gt;

&lt;p&gt;In PowerShell profile:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Helper functions for building the class&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nv"&gt;$script&lt;/span&gt;:nativeMethods &lt;span class="o"&gt;=&lt;/span&gt; @&lt;span class="o"&gt;()&lt;/span&gt;;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;Register-NativeMethod&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$dll&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$methodSignature&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="nv"&gt;$script&lt;/span&gt;:nativeMethods +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;PSCustomObject]@&lt;span class="o"&gt;{&lt;/span&gt; Dll &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$dll&lt;/span&gt;; Signature &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$methodSignature&lt;/span&gt;; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;Add-NativeMethods&lt;span class="o"&gt;()&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="nv"&gt;$nativeMethodsCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$script&lt;/span&gt;:nativeMethods | % &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        [DllImport(&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;.Dll&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="se"&gt;`"&lt;/span&gt;&lt;span class="s2"&gt;)]&lt;br data-jekyll-commonmark-ghpages="" /&gt;        public static extern &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;.Signature&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    "&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="nb"&gt;Add-Type&lt;/span&gt; @&lt;span class="s2"&gt;"&lt;br data-jekyll-commonmark-ghpages="" /&gt;        using System;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        using System.Runtime.InteropServices;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        public static class NativeMethods {&lt;br data-jekyll-commonmark-ghpages="" /&gt;            &lt;/span&gt;&lt;span class="nv"&gt;$nativeMethodsCode&lt;/span&gt;&lt;span class="s2"&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        }&lt;br data-jekyll-commonmark-ghpages="" /&gt;"&lt;/span&gt;@&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# Add methods here&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;Register-NativeMethod &lt;span class="s2"&gt;"user32.dll"&lt;/span&gt; &lt;span class="s2"&gt;"bool SetForegroundWindow(IntPtr hWnd)"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;Register-NativeMethod &lt;span class="s2"&gt;"user32.dll"&lt;/span&gt; &lt;span class="s2"&gt;"bool ShowWindowAsync(IntPtr hWnd, int nCmdShow)"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# This builds the class and registers them (you can only do this one-per-session, as the type cannot be unloaded?)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;Add-NativeMethods&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And to use them:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# (the Out-Null is just to throw away the return value)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;[&lt;/span&gt;NativeMethods]::SetForegroundWindow&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;Get-Process&lt;/span&gt; -name notepad&lt;span class="o"&gt;)&lt;/span&gt;.MainWindowHandle&lt;span class="o"&gt;)&lt;/span&gt; | Out-Null&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;[&lt;/span&gt;NativeMethods]::ShowWindowAsync&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;Get-Process&lt;/span&gt; -name notepad&lt;span class="o"&gt;)&lt;/span&gt;.MainWindowHandle, 2&lt;span class="o"&gt;)&lt;/span&gt; | Out-Null&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hopefully this helps a little. If anyone can suggest improvements (being able to paste method signatures directly from &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633539.aspx"&gt;the documentation&lt;/a&gt; would be good), I’ll update the post.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/10/easily-calling-windows-apis-from-powershell/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/AgRGd8jScII" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/AgRGd8jScII/" title="Easily Calling Windows APIs from PowerShell" />
	<feedburner:origLink>https://blog.dantup.com/2013/10/easily-calling-windows-apis-from-powershell/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-10-23:/2013/10/useful-powershell-profile-snippets/</id>
		<published>2013-10-23T00:00:00+00:00</published>
		<updated>2013-10-23T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="PowerShell" />
		
		<title type="text">Useful PowerShell Profile Snippets</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Yesterday I recevied an email about some code snippets in my PowerShell profile that I’d mentioned on StackOverflow. While trying to retrieve them; I noticed that I hadn’t put them on to my newly-installed Windows 8.1 machine; so I thought it was worth sharing them here while I was copying them from my work machine anyway.&lt;/p&gt;

&lt;p&gt;I don’t use PowerShell for scripting much now; I’ve been using FSI (type safety FTW). However, I do still think PowerShell is an excellent shell, and the ability to connect remotely to servers is incredibly useful for executing our deployment/IIS setup scripts/etc.&lt;/p&gt;

&lt;h2 id="adding-colour-to-hostnames-in-powershell-remoting-windows"&gt;Adding Colour to Hostnames in PowerShell Remoting Windows&lt;/h2&gt;

&lt;p&gt;With some help from &lt;a href="http://stackoverflow.com/q/13689797"&gt;StackOverflow&lt;/a&gt;, I have some functions to help me connect to frequently-used servers, highlighting the hostname depending on whether the server is local/staging/live (green/yellow/red). They’re all prefixed with R- so I can type R- and then tab through the different servers.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Connect to servers&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;R&lt;/span&gt;-LocalQAServer    &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;R&lt;/span&gt;-_Connect Green  &lt;span class="s2"&gt;"LocalQAServer"&lt;/span&gt;       &lt;span class="nv"&gt;$null&lt;/span&gt;          &lt;span class="s2"&gt;"D:\Applications\MyApps"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;R&lt;/span&gt;-BuildServer2     &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;R&lt;/span&gt;-_Connect Green  &lt;span class="s2"&gt;"buildserver2"&lt;/span&gt;        &lt;span class="nv"&gt;$null&lt;/span&gt;          &lt;span class="s2"&gt;"D:\CI"&lt;/span&gt;               &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;R&lt;/span&gt;-StageWebserver   &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;R&lt;/span&gt;-_Connect Yellow &lt;span class="s2"&gt;"stageweby.mycompany"&lt;/span&gt; &lt;span class="s2"&gt;"mycompany\me"&lt;/span&gt; &lt;span class="s2"&gt;"D:\Applications\MyApps"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;R&lt;/span&gt;-ProductionServer &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;R&lt;/span&gt;-_Connect Red    &lt;span class="s2"&gt;"live.mycompany"&lt;/span&gt;      &lt;span class="s2"&gt;"mycompany\me"&lt;/span&gt; &lt;span class="s2"&gt;"D:\Applications\MyApps"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# Shared function to handle connecting to servers with specified colour and a specific starting directory&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;R&lt;/span&gt;-_Connect&lt;span class="o"&gt;([&lt;/span&gt;ConsoleColor]&lt;span class="nv"&gt;$color&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$hostname&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$root&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="k"&gt;If&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; New-PSSession &lt;span class="nv"&gt;$hostname&lt;/span&gt; -Credential &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="k"&gt;Else&lt;/span&gt;       &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; New-PSSession &lt;span class="nv"&gt;$hostname&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# Set up MYAPP:\ Drive&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Invoke-Command -Session &lt;span class="nv"&gt;$session&lt;/span&gt; -ScriptBlock &lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;New-PSDrive&lt;/span&gt; -Name MYAPP -PSProvider FileSystem -Root &lt;span class="nv"&gt;$using&lt;/span&gt;:root | Out-Null&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;CD &lt;/span&gt;MYAPP:\&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# Pass the hostname+color in so we know what was connected to, so we can overwrite the prompt in colour&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Invoke-Command -Session &lt;span class="nv"&gt;$session&lt;/span&gt; -ScriptBlock &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$connectedHost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$using&lt;/span&gt;:hostname &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Invoke-Command -Session &lt;span class="nv"&gt;$session&lt;/span&gt; -ScriptBlock &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$promptColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="nv"&gt;$using&lt;/span&gt;:color &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# Overwrite the prompt function to colour the server name&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Invoke-Command -Session &lt;span class="nv"&gt;$session&lt;/span&gt; -ScriptBlock &lt;span class="o"&gt;([&lt;/span&gt;ScriptBlock]::Create&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"function prompt { &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;Get-Content &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;:\RemotePrompt&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; }"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# Connect the local/remote sessions&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Enter-PSSession -Session &lt;span class="nv"&gt;$session&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# Helper function that is transferred to the remote server to rewrite the prompt with colour&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;RemotePrompt &lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# write computer name with color&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;env&lt;/span&gt;:computername&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;env&lt;/span&gt;:username&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)]: "&lt;/span&gt; -Fore &lt;span class="nv"&gt;$promptColor&lt;/span&gt; -NoNew&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# generate regular prompt you would be showing&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nv"&gt;$defaultPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PS &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$executionContext&lt;/span&gt;.SessionState.Path.CurrentLocation&lt;span class="k"&gt;)$(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;'&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$nestedPromptLevel&lt;/span&gt; + 1&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;) "&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# generate backspaces to cover [computername]: pre-prompt printed by powershell&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="k"&gt;If&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nv"&gt;$connectedHost&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$connectedHost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$env&lt;/span&gt;:Computername &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nv"&gt;$backspaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$connectedHost&lt;/span&gt;.Length + 4&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="c1"&gt;# compute how much extra, if any, needs to be cleaned up at the end&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nv"&gt;$remainingChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Math]::Max&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$connectedHost&lt;/span&gt;.Length + 4&lt;span class="o"&gt;)&lt;/span&gt; - &lt;span class="nv"&gt;$defaultPrompt&lt;/span&gt;.Length, 0&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nv"&gt;$tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$remainingChars&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; + &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`b&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$remainingChars&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;backspaces&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;defaultPrompt&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;tail&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="launch-kilnbitbucketwhatever-from-shell"&gt;Launch Kiln/BitBucket/Whatever from Shell&lt;/h2&gt;

&lt;p&gt;The first thing Iusually do after pushing changes to Kiln, is open up the web app and raise code reviews. So; I added a command “Kiln” which just launches Kiln at the correct repo page for where I am. The Kiln extension can do this (“hg kiln”), but we’ve had bad experiences with the Kiln extensions not working on recent versions of Mercurial (due to PyWin32 dependencies) so most of us have them all disabled (except KilnAuth).&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Launch Kiln for current repo&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;Kiln&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Test-Path&lt;/span&gt; &lt;span class="s2"&gt;".\.hg\hgrc"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nv"&gt;$repoUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Select-String&lt;/span&gt; &lt;span class="s2"&gt;"default = (.*)"&lt;/span&gt; -Path &lt;span class="s2"&gt;".\.hg\hgrc"&lt;/span&gt; -AllMatches&lt;span class="o"&gt;)&lt;/span&gt;.Matches.Groups[1].Value&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;Start&lt;/span&gt; &lt;span class="nv"&gt;$repoUrl&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="k"&gt;else&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;Write-Warning&lt;/span&gt; &lt;span class="s2"&gt;"Not in a repo!"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="launch-visual-studio-for-current-project"&gt;Launch Visual Studio for Current Project&lt;/h2&gt;

&lt;p&gt;Because I switch between repos a lot (different versions of our product), it’s a pain to change directory in PoSh and also navigate to the new solution in Visual Studio. So I simply close Visual Studio and type “VS” when in the correct directory for the new branch to launch Visual Studio for any sln file in the current folder. This could be extended to recurse down (or up), but I found it most convenient just working in the immediate folder.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Launch VS for sln(s) in current folder&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;VS&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;ii&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.sln&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="launch-windows-explorer-for-current-folder"&gt;Launch Windows Explorer for current folder&lt;/h2&gt;

&lt;p&gt;Why type “start .” when you can just type “e” to start explorer in the current folder?&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Launch explorer in current folder&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;e &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;ii&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="launch-default-browser-for-current-folder-via-web-server"&gt;Launch default browser for current folder via web server&lt;/h2&gt;

&lt;p&gt;Sometimes you have a static html file you want to load up in a browser to hack on and test, but some browsers (like Chrome) won’t let you pull in scripts for file:/// paths. This function (which requires Python in your PATH) launches the builtin Python web server for the current folder and fires up your browser. If you pass an optional filename&lt;/p&gt;
&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;Serve mytest.htm&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;then it’ll load that fie in the browser.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="k"&gt;Function &lt;/span&gt;Serve &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;Start-Process &lt;/span&gt;python -ArgumentList &lt;span class="s2"&gt;"-m SimpleHTTPServer 8000"&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;Start&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:8000/&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;	&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="run-mercurial-commands-with-less-typing"&gt;Run Mercurial Commands with Less Typing!&lt;/h2&gt;

&lt;p&gt;And while we’re at it; why have to type “hg” all the time?&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Mercurial helpers&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;st &lt;span class="o"&gt;{&lt;/span&gt; hg st &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;vd &lt;span class="o"&gt;{&lt;/span&gt; hg vd &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;ci &lt;span class="o"&gt;{&lt;/span&gt; hg ci &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;clone &lt;span class="o"&gt;{&lt;/span&gt; hg clone &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;hist &lt;span class="o"&gt;{&lt;/span&gt; hg hist -l 5 &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="add-visual-studio--net-tools-to-path"&gt;Add Visual Studio / .NET Tools to PATH&lt;/h2&gt;

&lt;p&gt;I like the Visual Studio Developer Command Prompt because it has lots of useful stuff in PATH; but trying to get this into PoSh sucks. You can’t call the old .bat files because they won’t update PowerShells PATH. Re-implementing them is insane, so it’s easier to just hard-code the important few, and add/update as required!&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Add some useful stuff to PATH&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;:Path +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;";C:\Program Files (x86)\Microsoft SDKs\F#\3.1\Framework\v4.0\; C:\windows\Microsoft.NET\Framework\v4.0.30319; C:\windows\Microsoft.NET\Framework\v3.5; C:\Program Files (x86)\Windows Kits\8.1\bin\x86;"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="clean-all-folders-with-msbuild"&gt;Clean all folders with MSBuild&lt;/h2&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Reclaim a ton of disk space from branches that have been built but aren't actively needed&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# It's hard-coded with my code folder and MYAPP:\ so I can call it from anywhere, but&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# it can be made more generic and simplified with James Tryand's walk function further down&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;Function &lt;/span&gt;Clean-All&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;pushd &lt;/span&gt;MYAPP:\&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;Get-ChildItem&lt;/span&gt; -Directory | % &lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;Get-Item&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.sln | % &lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"    Cleaning {0}"&lt;/span&gt; -f &lt;span class="nv"&gt;$_&lt;/span&gt;.ToString&lt;span class="o"&gt;()&lt;/span&gt;.Replace&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"C:\Work\Source\MYAPP\"&lt;/span&gt;, &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; -F Yellow&lt;br data-jekyll-commonmark-ghpages="" /&gt;			&amp;amp;&lt;span class="s1"&gt;'msbuild'&lt;/span&gt; /t:Clean /nologo /v:m &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;		&lt;span class="nb"&gt;popd&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;	&lt;span class="nb"&gt;popd&lt;br data-jekyll-commonmark-ghpages="" /&gt;	Write-Host&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id="create-a-psdrive-for-code-folder"&gt;Create a PSDrive for Code Folder&lt;/h2&gt;

&lt;p&gt;To make it easy to always get back to my root code folder, I’ve created a PDrive, and change into it at launch.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Set up MYAPP:\ to point at code checkout folder&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;New-PSDrive&lt;/span&gt; -Name MYAPP -PSProvider FileSystem -Root C:\Work\Source\MYAPP | Out-Null&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;CD &lt;/span&gt;MYAPP:\&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I think that’s all the useful stuff incurrently in my profile that might be handy to others. Do post your own useful snippets in the comments!&lt;/p&gt;

&lt;h2 id="highlights-from-the-comments"&gt;Highlights from the Comments&lt;/h2&gt;

&lt;h3 id="execute-command-for-all-child-directories"&gt;Execute command for all child directories&lt;/h3&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="k"&gt;Function &lt;/span&gt;Walk-ChildDirectory&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="k"&gt;Param&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="o"&gt;[&lt;/span&gt;Parameter&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$true&lt;/span&gt;,ValueFromPipeline&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$true&lt;/span&gt;&lt;span class="o"&gt;)][&lt;/span&gt;ScriptBlock]&lt;span class="nv"&gt;$Task&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="nb"&gt;ls&lt;/span&gt; -Directory | %&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &amp;amp; &lt;span class="nv"&gt;$Task&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="nb"&gt;popd&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;Set-Alias &lt;/span&gt;walk Walk-ChildDirectory&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# eg.:&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# walk { git gc } # Git GC (WTF is this?)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# walk { msbuild /t:clean } # Clean all projects to reclaim disk space!&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;# walk { walk { walk { pwd } } } # Can be nested!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id="ensure-stuff-doesnt-drop-off-the-history-list"&gt;Ensure stuff doesn’t drop off the history list&lt;/h3&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="nv"&gt;$MaximumHistoryCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1024&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id="handy-functions-to-handle-strings-more-nicely"&gt;Handy functions to handle strings more nicely&lt;/h3&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="k"&gt;function &lt;/span&gt;Quote-String &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;function &lt;/span&gt;Quote-List &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;Set-Alias &lt;/span&gt;qs Quote-String&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="nb"&gt;Set-Alias &lt;/span&gt;ql Quote-List&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/10/useful-powershell-profile-snippets/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/Q6qO-eP97F8" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/Q6qO-eP97F8/" title="Useful PowerShell Profile Snippets" />
	<feedburner:origLink>https://blog.dantup.com/2013/10/useful-powershell-profile-snippets/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-09-20:/2013/09/functional-programming-challenge-words-with-indexes/</id>
		<published>2013-09-20T00:00:00+00:00</published>
		<updated>2013-09-20T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="F#" />
		
		<title type="text">Functional Programming Challenge; Words with Indexes</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;A colleague has also been starting to learn F#, and today gave me a problem he’d been trying to solve in F# in a “functional style”. He wanted a function that took a string input, and returned a list of the words, tupled with the starting index.&lt;/p&gt;

&lt;p&gt;I spent the whole of lunch staring at my screen, trying to wrap my head around List.fold, list.foldBack and other functions; but no joy.&lt;/p&gt;

&lt;p&gt;While driving home, it occurred to me that I could do away with that nonsense, and just have a recursive function that passes all the values down, top-to-bottom!&lt;/p&gt;

&lt;p&gt;Here’s my latest attempt… The results are correct, which is an improvement over my original attempt!&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;splitOn&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;takeWhile&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skipWhile&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isSpace&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;notSpace&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toArray&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="c1"&gt;/// Get a list of all words in a string tupled with the starting index&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getWords&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;getWordsRec&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitOn&lt;/span&gt; &lt;span class="n"&gt;isSpace&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitOn&lt;/span&gt; &lt;span class="n"&gt;notSpace&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;            &lt;span class="n"&gt;getWordsRec&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;                &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="n"&gt;getWordsRec&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"World"&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="n"&gt;printfn&lt;/span&gt; &lt;span class="s2"&gt;"Result is %A"&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;getWords&lt;/span&gt; &lt;span class="s2"&gt;"Hello test  World"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Can anyone come up with something more elegant?&lt;/p&gt;

&lt;h2 id="update"&gt;Update&lt;/h2&gt;

&lt;p&gt;I managed to simplify/flatten it a little, though I’m not convinced it’s any more readable!&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toArray&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getWords&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="n"&gt;input&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initInfinite&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Zip the list with indexes&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scan&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Scan; replacing  index with the previous index when not a space&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="o"&gt;(_,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Strip the spaces&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupBy&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;_)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Group by the word start index&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;snd&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Strip redundant indexes; convert to string&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/09/functional-programming-challenge-words-with-indexes/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/G3ULxHDfvC8" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/G3ULxHDfvC8/" title="Functional Programming Challenge; Words with Indexes" />
	<feedburner:origLink>https://blog.dantup.com/2013/09/functional-programming-challenge-words-with-indexes/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-09-14:/2013/09/opt-in-nulls-an-fsharp-feature-worth-switching-for/</id>
		<published>2013-09-14T00:00:00+00:00</published>
		<updated>2013-09-14T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="F#" />
		
		<title type="text">Opt-in Nulls; an F# Feature Worth Switching For?</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Recently I’ve been learning a little F#. This is actually the third time I’ve tried to pick it up… The first two times I just couldn’t get my head around it. This time, I think it’s making sense! Whether or not I ever get to use it commercially/in production, I think it’s been a valuable excercise. Just the basics of F# I’ve picked up so far have already start influencing how I’m writing C# in a good way.&lt;/p&gt;

&lt;p&gt;There are a &lt;em&gt;lot&lt;/em&gt; of things in F# that would save me a lot of time and/or drastically improve quality. One such feature is the handling of “nulls” using the &lt;code class="highlighter-rouge"&gt;Option&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;The &lt;code class="highlighter-rouge"&gt;Option&lt;/code&gt; type is similar to the &lt;code class="highlighter-rouge"&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; type used nullable with value types in .NET, but it can be applied to reference types too. Because of this, types are (by default) not nullable in F# ( &lt;em&gt;generally&lt;/em&gt; …).&lt;/p&gt;

&lt;p&gt;Take the following code that prints out a customers name:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-fsharp" data-lang="fsharp"&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;printCustomerName&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages="" /&gt;    &lt;span class="n"&gt;printfn&lt;/span&gt; &lt;span class="s2"&gt;"Customer name is %s"&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Name&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In C#, this code would need to have a null check before accessing the &lt;code class="highlighter-rouge"&gt;Name&lt;/code&gt; property to avoid a possible &lt;code class="highlighter-rouge"&gt;NullReferenceException&lt;/code&gt;. In F#, because the argument is a &lt;code class="highlighter-rouge"&gt;Customer&lt;/code&gt; and not a &lt;code class="highlighter-rouge"&gt;Customer option&lt;/code&gt;, this check is not needed. The F# compiler will ensure that only &lt;code class="highlighter-rouge"&gt;Customer&lt;/code&gt; types can be passed as an argument to this function (note: if you’re consuming this code from another .NET language, all bets are off!).&lt;/p&gt;

&lt;p&gt;I recently gave a talk to my colleagues about some of the interesting F# features I’d come across so far; the &lt;code class="highlighter-rouge"&gt;Option&lt;/code&gt; type being one of them. After the talk, one of my colleagues was debugging a &lt;code class="highlighter-rouge"&gt;NullReferenceException&lt;/code&gt; and I (jokingly) said that every time I hear someone debugging a &lt;code class="highlighter-rouge"&gt;NullReferenceException&lt;/code&gt; or see a null check, I’m going to exclaim “You wouldn’t have that in F#!”.&lt;/p&gt;

&lt;p&gt;Well; it’s been only a week. Already; the number of times this thought has popped into my head is &lt;em&gt;way&lt;/em&gt; above what I thought it would be. The number of null checks going into our code &lt;em&gt;on a daily basis&lt;/em&gt; and the number of &lt;code class="highlighter-rouge"&gt;NullReferenceException&lt;/code&gt;s raised by our (sadly, very legacy) codebase is staggering. I don’t know why we put up for this crap for so long! Runtime &lt;code class="highlighter-rouge"&gt;NullReferenceException&lt;/code&gt;s are embarassing, and thousands of null checks just add to code noise. Why can’t we have our cake and eat it?&lt;/p&gt;

&lt;p&gt;This is just one of many F# features that I geniunely believe could have a big enough impact on your code (both quality, and readability) that it should be a factor when picking your programming language. It has access to the same BCL that C# does. It has access to the same third party libraries and NuGet packages. It can even interact with your existing C# code. Unfortunately, there are still some significant reasons not to switch; the tooling is nowhere near as good as with C# (even only considering stock VS functionality), and StackOverflow has only a fraction of the answers you’ll want in F# vs C#. Both of these will improve in time and I think the idea of using F# for much of your .NET coding will become even more compelling.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/09/opt-in-nulls-an-fsharp-feature-worth-switching-for/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/K5uYdyUqmZA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/K5uYdyUqmZA/" title="Opt-in Nulls; an F# Feature Worth Switching For?" />
	<feedburner:origLink>https://blog.dantup.com/2013/09/opt-in-nulls-an-fsharp-feature-worth-switching-for/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-09-08:/2013/09/migrating-blog-to-github-pages-and-jekyll/</id>
		<published>2013-09-08T00:00:00+00:00</published>
		<updated>2013-09-08T00:00:00+00:00</updated>
		
			<category term="Deployment" />
		
			<category term="Google App Engine" />
		
			<category term="GitHub" />
		
			<category term="Open Source" />
		
			<category term="Blogging" />
		
		<title type="text">Migrating my blog from Google App Engine to GitHub Pages and Jekyll</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I’ve been trying to migrate my blog from Google App Engine to something a little more manageable (and not tied to GAE infrastructure) for some time. However, all of my attempts to rewrite the blog in ASP.NET have failed due to newer versions of “things” coming out, making me start over before I get to the end. I’ve come to the conclusion that coding my own blog is getting in the way of me blogging.&lt;/p&gt;

&lt;p&gt;However; yesterday I came across Jekyll, which runs on GitHub Pages. This combination solves a lot of my blog requirements without me having to do anything!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s free to host on GitHub pages&lt;/li&gt;
  &lt;li&gt;All of my existing posts can stay in HTML&lt;/li&gt;
  &lt;li&gt;My new posts can be written in Markdown! (finally! this one is completely Markdown :))&lt;/li&gt;
  &lt;li&gt;It’s not tied to GitHub, or even Jekyll. I can take the static HTML version of my site and put it anywhere (or replace it with a dynamic one in future!)&lt;/li&gt;
  &lt;li&gt;Code formatting support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully; if my blog is low-maintenance, I might actually get back to blogging a little more! Sadly, there are a few little drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Jekyll doesn’t support tag/category listing/index pages out-of-the-box, and GitHub has plugins disabled. I came up with a workaround for this (as you can see, I have tags in the sidebar that link), but it relies on creating a stub page for each tag; but that doesn’t seem too bad compared to having to run Jekyll locally and just publish the build output! I might blog the details of this once everything is fully working.&lt;/li&gt;
  &lt;li&gt;All of my posts (including those that have .html extensions) end up with trailing slashes in Jekyll. I can’t explain this, but they get 301 redirects due to the folder names, so I don’t think it’s a huge problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve still got some tidying up to do, but maybe now that my blog posts can just be text files in a repo, I’ll blog a little more!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/09/migrating-blog-to-github-pages-and-jekyll/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/wjAz9yq-csE" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/wjAz9yq-csE/" title="Migrating my blog from Google App Engine to GitHub Pages and Jekyll" />
	<feedburner:origLink>https://blog.dantup.com/2013/09/migrating-blog-to-github-pages-and-jekyll/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-06-11:/2013/06/fixing-duplicate-contacts-in-android-people-app-from-google-contacts-and-google/</id>
		<published>2013-06-11T00:00:00+00:00</published>
		<updated>2013-06-11T00:00:00+00:00</updated>
		
			<category term="Android" />
		
			<category term="Google+" />
		
		<title type="text">Fixing Duplicate Contacts in Android People app from Google Contacts and Google+</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Now that I've swapped my Windows Phone for a Nexus 4, I've discovered what a mess my Google Contacts are in once synced to Android and enabling Google+ to supplement contact data. It didn't matter too much on the &lt;a href="http://prodct.info/nexus7"&gt;Nexus 7&lt;/a&gt; tablet, but on the phone it's more frequently used, so having the same contacts appear three times is a problem!&lt;/p&gt;

&lt;p&gt;Note: The People app has a "join" feature that lets you merge contacts on the device; however this is stored on the device and not synced anywhere. With multiple devices (and the possibility of buying new devices/factory resets/etc.), this isn't an ideal solution, so I wanted to fix the problem at the source.&lt;/p&gt;

&lt;p&gt;I did all the usual stuff (merging duplicates in the Google Contacts web app), but still found some contacts where I had 2, 3 or even 4 copies once they made it to Android! I spent many hours going through making tiny changes to contacts to figure out what was causing the duplicates, and I've listed all the things that bit me below.&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;
&lt;strong&gt;"Email Contacts" in Google+ Circles&lt;/strong&gt;&lt;br /&gt;
&lt;small&gt;I found a number of circles that had "email" contacts in. When Google+ first launched, I just put people suggested by Google into circles. These often turned out to be non-Gmail email addresses; and the person subsequently joined Google+, making two contacts with the same name/email. The fix is easy; just remove all email contacts from circles.&lt;/small&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;strong&gt;People with multiple Google+ Profiles&lt;/strong&gt;&lt;br /&gt;
&lt;small&gt;Because Google are pushing us into upgrading all Google accounts into Google+ Profiles (eg. you can't share pictures with Hangouts without this; despite it working in Google Talk), many people have two accounts (especially if they use Google Apps for work). To fix this; I just uncircled all but what appeared to be the "main" account for anybody with multiple.&lt;/small&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;strong&gt;Google Contact records with different names to Google+&lt;/strong&gt;&lt;br /&gt;
&lt;small&gt;This one is pretty stupid. I had my parents named "Mum" and "Dad" in Gooogle Contacts, and some contacts as Mike and Matt that were Michael and Matthew in Gooogle+. This seemed to confuse the Google+ sync, and showed them as separate contacts in the People app. I couldn't find a fix for this; so mum and dad now have their full names in People :(&lt;/small&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;strong&gt;Google+ syncs stale data!&lt;/strong&gt;&lt;br /&gt;
&lt;small&gt;This one bit me the hardest. Every time I thought I'd fixed the issue, dupes would reappear a few minutes later. I believe the problem was that the Google+ app cached circle data (so all my changes to circles for the above where stale) and then syncs it into People. To fix this, after each change, I did "Clear Data" on the Google+ app (in addition to the Contacts and Contacts Storage).&lt;/small&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;strong&gt;Multiple profile URLs against a Google Contact&lt;/strong&gt;&lt;br /&gt;
&lt;small&gt;This is similar to a contact having multiple Google+ accounts. I found that when I'd added some contacts to circles, it had copied their profile URL into the contact record (under URLs, with a type of Profile). When I'd merged contacts, I ended up with multiple of these against the same record. This also caused the contact to appear twice! Fixing this was easy; I just deleted all profile URLs against all contacts, unless it was the only piece of information tying them to their Google+ profile (in most cases, I had the email address, so this was not needed).&lt;/small&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After all these changes, I've cleared data/caches and forced syncs several times no both devices, and haven't (yet) seen any contacts duplicated again :-)&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/06/fixing-duplicate-contacts-in-android-people-app-from-google-contacts-and-google/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/p0cpbbtoGWo" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/p0cpbbtoGWo/" title="Fixing Duplicate Contacts in Android People app from Google Contacts and Google+" />
	<feedburner:origLink>https://blog.dantup.com/2013/06/fixing-duplicate-contacts-in-android-people-app-from-google-contacts-and-google/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-06-11:/2013/05/farewell-windows-phone-you-cant-say-i-didnt-try-hello-android/</id>
		<published>2013-06-11T00:00:00+00:00</published>
		<updated>2013-06-11T00:00:00+00:00</updated>
		
			<category term="Android" />
		
			<category term="Windows Phone" />
		
		<title type="text">Farewell Windows Phone (You can't say I didn't try!). Hello Android!</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;After the craziness of a previous Windows Phone blog entry (&lt;small&gt;"&lt;a href="/2011/03/why-im-close-to-giving-up-on-windows-phone-7-as-a-user-and-a-developer/"&gt;Why I'm Close to Giving Up on Windows Phone 7, as a User and a Developer&lt;/a&gt;"&lt;/small&gt;), I thought it was worth posting an update, now that my "relationship" with Windows Phone has come to an end. Recently I placed an order for a &lt;a href="https://play.google.com/store/devices/details?id=nexus_4_16gb"&gt;Google/LG Nexus 4 Android phone&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;You can't say I didn't give Microsoft a fair shot. You can &lt;a href="/2011/03/why-im-close-to-giving-up-on-windows-phone-7-as-a-user-and-a-developer/"&gt;read the full history in this post&lt;/a&gt;; and I continued to use Windows Phone for more than another two years since then. Windows Phone wasn't the great MS phone it should've been (and that I was expecting it to be). It's time to move on. I know a lot of naysayers in the comments of the original post will be saying "Told you so", and you were right. But I had to give it a try :)&lt;/p&gt;

&lt;p&gt;Anyone that follows me on &lt;a href="https://twitter.com/DanTup"&gt;Twitter&lt;/a&gt; or &lt;a href="https://plus.google.com/113181962167438638669/"&gt;Google+&lt;/a&gt; probably knows this has been a long time coming. I've been a big fan of Android since swapping my iPad for a &lt;a href="http://prodct.info/nexus7"&gt;Nexus 7&lt;/a&gt;. In many ways, Windows Phone feels to me now, how my iPad felt to me then; kinda restricted and "doesn't do a lot". I expect the change to Android for my phone will bring the same sort of advantages and increased *ahem* "productivity" (where productivity means, email, social, gaming and other non-productive stuff) that changing my tablet gave.&lt;/p&gt;

&lt;p&gt;To be constructive, here's some of the reasons:&lt;/p&gt;

&lt;h2&gt;Reasons Android seems better to me than Windows Phone&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Not enough investment from Microsoft in Windows Phone.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;Only a few major updates since launch, and they added nonsense stuff like different sized icons and made the browser less crappy.&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not enough investment from third parties in Windows Phone.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;eg. Google just launched Hangouts on Android, iOS, Web, but no Windows Phone; no Google+ on Windows Phone. It's not just Google; other big apps are still missing.&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;My annoying Bluetooth issue still exists in Windows Phone.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;Even on the Lumia 900 I was Kindly sent by Microsoft (they thought my issue was isolated to HTC, not Windows Phone) it's still bust, and there's no way I can help debug :o( It's early days, but I haven't yet had the issue with Android, so it seems more likely WP than my Car (the problem also didn't occur on iPhone).&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microsoft Web apps are clunky.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;Google's web apps (Gmail, GDocs/Drive, Calendar) all work really well (even if they're not very pretty), and sync nicely with the Android apps. The last time I tried the equiv online apps from Microsoft (Hotmail/Outlook, online Office) they were very fat and clunky. Being able to do things on the web when I'm at a PC is a big deal; some things are just tiresome on a mobile device. I want to be able to switch devices seamlessly.&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Hangouts!&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;It's only just launched, but I think Hangouts have a bright future. It's like iMessage, but works on iOS, Android and the web! It's a real bummer that Google didn't include Windows Phone support, but I'm really liking this a lot. The way mobile/desktop work together is very nice; such as not appearing "online" from a mobile device, which cuts down on random "chat" messages. Being able to switch device between PC, Tablet, Mobile is killer.&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bluetooth Audio Streaming Quality is terrible on Windows Phone&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;The audio quality of BT streaming on my Windows Phones was terrible. This wasn't so bad for phone calls; but made it unusable for music. I thought it might've been HTC-specific, but had the same reason with the Lumia. My Nexus has no such issue; and I'm able to happily play music from Google Music without any quality issues :-)&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster Access to Updates&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;With a Nexus device, I know I'll get OS updates fairly quickly. Microsoft are still bending over for carriers and letting them block updates. No excuse for this.&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many other niggles I had with Windows Phone; I find myself rarely using it for anything other than calls/texts because it feels limited and slow. Whenever I go to the Windows Phone marketplace, I'm amazed at how basic it is; how little information/options/etc. (and it's so slow!). It almost feels unfinished :(&lt;/p&gt;

&lt;p&gt;Additionally, I'm quite a big user of Google services (eg.; Gmail/Calendar/Docs), so having native apps is nice (for example; the Gmail app has nice support for Gmail-native features, like switching From addresses and the new automatic categories).&lt;/p&gt;

&lt;p&gt;The auto-backup of photos with Google+ works really well too; and ironically, the day after receiving my Nexus 4, my daughter spilled juice over my wife's Windows Phone and completely fried it, and we've been unable to recover the photos from it. I know Windows Phone can auto-backup too (wife's fault for not enabling it, or mine for not telling her, I guess!), but I believe it won't do full-res versions of the photo, which is kinda crap.&lt;/p&gt;

&lt;p&gt;That said; I must say there are things I'm going to miss...&lt;/p&gt;

&lt;h2&gt;Things I'll miss about Windows Phone&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SMS messages read to me in my car.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;I love that Windows Phone has built-in functionality to read text messages to me via Bluetooth in my car. Replying has never worked for me (it can never tell what I'm saying, and just rudely hangs up after a few tries), but just having them read has been very useful. Looks like there are third party apps that do this on Android, but there's always that additional confidence you have in built-in functionality :(&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The dedicated camera button.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;This is a big one for me, because I have a 1yr old daughter and a week old son. Being able to launch the camera really quickly is killer.&lt;/small&gt;&lt;br /&gt;&lt;small&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; Since receiving my Nexus 4, I discovered there was a pre-configured Camera widget on the lock screen; I just tap the power button, then swipe left, and it launches the camera. Although it's two actions, it's about the same (or less) time than the WP long-press on the camera button, so it's actually not been the issue I thought it would :-)&lt;/em&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The people hub.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;Having my phone know that this email address, this twitter user, this facebook user etc. are all the same person is killer. Android + iOS have work to do here. Facebook doesn't sync contacts (despite showing the option too), nor does Twitter. My People app just shows Google contacts :(&lt;/small&gt;&lt;br /&gt;&lt;small&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; Since receiving my Nexus 4, I discovered that Android does support this; but Facebook and Twitter just don't use it (though there are third party apps for Facebook at least). So it's not as bad as I thought, especially as I had Twitter contacts hidden on WP because there are so many people I don't really "know".&lt;/em&gt;&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Tiles.&lt;/strong&gt;&lt;br /&gt;&lt;small&gt;Not for anything useful, but little touches like the Pictures tile showing a random photo I've taken (usually of my daughter pulling a cheeky face); really makes me smile :-) There are some photo widgets on Android, but none of them really behave in a similar way that I find them worth having.&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/05/farewell-windows-phone-you-cant-say-i-didnt-try-hello-android/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/XetmJ-ljZg8" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/XetmJ-ljZg8/" title="Farewell Windows Phone (You can't say I didn't try!). Hello Android!" />
	<feedburner:origLink>https://blog.dantup.com/2013/05/farewell-windows-phone-you-cant-say-i-didnt-try-hello-android/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-06-09:/2013/06/fix-for-google-music-showing-prices-in-usd-and-us-onyservices-for-non-us-users/</id>
		<published>2013-06-09T00:00:00+00:00</published>
		<updated>2013-06-09T00:00:00+00:00</updated>
		
			<category term="Google Music" />
		
		<title type="text">Fix for Google Music showing prices in USD and US-only services for non-US users</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Many people gained access to Google Music before it launched in their country by first accessing it from the US (sometimes via a VPN, EC2 instance, etc.). When Google Music launched "for real" in their countries, many (like me), found that that service believed they were in the US, and offered US-only services (such as All Access), and showed US-only offers, and prices in USD.&lt;/p&gt;

&lt;p&gt;After many months of exchanging emails with Google Play support and trying all sorts of things, I was ultimately (and repeatedly) told they were aware of the issue, but did not yet have a fix :(&lt;/p&gt;

&lt;p&gt;Recently, I was able to solve this issue, with the help from &lt;a href="http://forum.xda-developers.com/showthread.php?p=42271779#post42271779"&gt;some info&lt;/a&gt; posted to an XDA-Developers forum by a poster named Movisman. Although the instructions didn't work for me as posted, it triggered a thought that lead to the fix. It actually turned out to be quite a simple fix:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchase a non-free track, using a card based on your home country as payment. Do not use Google Play credit!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All of my previous purchases had used the free credit that I got when I purchased my &lt;a href="http://prodct.info/nexus7"&gt;Nexus 7&lt;/a&gt;. As soon as I purchased a track using a UK card, the options for All Access vanished and all the recommendation prices changed from USD to GBP!&lt;/p&gt;

&lt;p&gt;Since &lt;a href="http://forum.xda-developers.com/showthread.php?p=42289354#post42289354"&gt;posting this fix in the forum&lt;/a&gt;, a number of other people have repeated this success. I'm posting it here in the hope it'll turn up in Google searches to help others. I've also notified Google Play support of how we fixed it (and provided a link to the forum) in the hope they can more quickly give a fix to anybody else reporting it.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/06/fix-for-google-music-showing-prices-in-usd-and-us-onyservices-for-non-us-users/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/0i9mP0YdT3M" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/0i9mP0YdT3M/" title="Fix for Google Music showing prices in USD and US-only services for non-US users" />
	<feedburner:origLink>https://blog.dantup.com/2013/06/fix-for-google-music-showing-prices-in-usd-and-us-onyservices-for-non-us-users/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2013-04-22:/2013/04/powershell-function-to-launch-kiln-bitbucket-github-google-code-etc-for-current-repo-from-command-line/</id>
		<published>2013-04-22T00:00:00+00:00</published>
		<updated>2013-04-22T00:00:00+00:00</updated>
		
			<category term="BitBucket" />
		
			<category term="Google Code" />
		
			<category term="Open Source" />
		
			<category term="PowerShell" />
		
			<category term="Kiln" />
		
			<category term="Git" />
		
			<category term="Mercurial" />
		
		<title type="text">PowerShell function to launch Kiln/BitBucket/Google Code/etc. for current Mercurial repo from command line</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;A small, but useful, PowerShell function that I have in my PowerShell profile that reads the default repo path from .hg\hgrc and launches it in the default browser. This means after I've done &lt;code&gt;hg push&lt;/code&gt; I can just ype &lt;code&gt;kiln&lt;/code&gt; to quickly get to the repo page to raise code reviews, etc.&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="c1"&gt;# Launch Kiln for current repo&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;&lt;span class="k"&gt;Function &lt;/span&gt;Kiln&lt;br data-jekyll-commonmark-ghpages&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Test-Path&lt;/span&gt; &lt;span class="s2"&gt;".\.hg\hgrc"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;	&lt;span class="o"&gt;{&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;		&lt;span class="nv"&gt;$repoUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Select-String&lt;/span&gt; &lt;span class="s2"&gt;"default = (.*)"&lt;/span&gt; -Path &lt;span class="s2"&gt;".\.hg\hgrc"&lt;/span&gt; -AllMatches&lt;span class="o"&gt;)&lt;/span&gt;.Matches.Groups[1].Value&lt;br data-jekyll-commonmark-ghpages&gt;		&lt;span class="nb"&gt;Start&lt;/span&gt; &lt;span class="nv"&gt;$repoUrl&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;	&lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;	&lt;span class="k"&gt;else&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;		&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;Write-Warning&lt;/span&gt; &lt;span class="s2"&gt;"Not in a repo!"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2013/04/powershell-function-to-launch-kiln-bitbucket-github-google-code-etc-for-current-repo-from-command-line/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/dRHdxkRS7-4" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/dRHdxkRS7-4/" title="PowerShell function to launch Kiln/BitBucket/Google Code/etc. for current Mercurial repo from command line" />
	<feedburner:origLink>https://blog.dantup.com/2013/04/powershell-function-to-launch-kiln-bitbucket-github-google-code-etc-for-current-repo-from-command-line/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2012-10-13:/2012/10/thoughts-on-typescript-dart-and-the-future-of-browser-based-coding/</id>
		<published>2012-10-13T00:00:00+00:00</published>
		<updated>2012-10-13T00:00:00+00:00</updated>
		
			<category term=".NET" />
		
			<category term="Dart" />
		
			<category term="TypeScript" />
		
			<category term="Open Source" />
		
			<category term="JavaScript" />
		
		<title type="text">Thoughts on TypeScript, Dart, and the future of browser-based coding</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;Had &lt;a href="http://twitter.theinfo.org/256885747091656704"&gt;a bit of a discussion&lt;/a&gt; on Twitter with &lt;a href="https://twitter.com/kdarty"&gt;Kevin Darty&lt;/a&gt; about TypeScript. It started off with this tweet:&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;
&lt;p&gt;Please RT if you actually know JavaScript and don't need a crutch like CoffeeScript, Dart or TypeScript.&lt;/p&gt;
&amp;mdash; Kevin Darty (&lt;a href="https://twitter.com/kdarty"&gt;@kdarty&lt;/a&gt;) &lt;a href="https://twitter.com/kdarty/status/256885747091656704" data-datetime="2012-10-12T22:35:09+00:00"&gt;October 12, 2012&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm still a bit puzzled by all the hate for TypeScript. There seem to be a lot of NaySayers, but I've still yet to see a single example of a codebase that couldn't benefit from TypeScript. Since it's a superset of JS, you don't have to do anything to start using it except rename your .js files to .ts, and then you can add TypeScript only where you think it will add value.&lt;/p&gt;

&lt;p&gt;It's like having a magic switch that helps you find errors and refactor. How can it possibly be a bad thing?&lt;/p&gt;

&lt;p&gt;I don't buy the "it'll stop people learning proper JavaScript" argument. I don't know a huge amount of Assembly or IL. Sure, there might be situations were knowing more of them would be useful, but is it really worth the time investment when I could instead be improving my C#/other skills that I use &lt;em&gt;every single day&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Does it matter if I can't type javascript without looking it up to hook up subclass prototypes in JavaScript if TypeScript can do it for me? No. Does it help if I understand the generated code? Sure. Is it a requirement? No. All that matters is I understand how it behaves and any limitations of it. The same way I don't need to understand 100% of the IL generated when I write C#.&lt;/p&gt;

&lt;p&gt;I'd actually argue that TypeScript will actually &lt;em&gt;improve&lt;/em&gt; most peoples ability to write this kind of JavaScript. Think of the workflow... Chances are, you'll store both .ts and .js files in source control (that's how I'd probably do it, so code reviews can see the actual code going to the browser, and we don't have to worry about devs seeing different code to what's being deployed by the CI server). That means when you're viewing your diffs prior to committing, you'll review both your changes and the generated changes from TypeScript. I'd say this will &lt;em&gt;teach&lt;/em&gt; you how these things work in JS. You'll become more aware of how sub-classes work, the behaviour of "this" in event handlers, etc.&lt;/p&gt;

&lt;p&gt;I'm still generally curious; I'd love to see someone present a codebase that can't benefit from TypeScript and prove me wrong. I genuinely can't think of how anyone could write JavaScript to run in the browser that wouldn't benefit from type-checking (whether it by their own objects, or access to the DOM) in any way. If you're a NaySayer, then take this challenge! :-)&lt;/p&gt;

&lt;p&gt;There was also some discussion about Dart following on from +Kevin Darty's tweet, like:&lt;/p&gt;

&lt;blockquote class="twitter-tweet" data-in-reply-to="256947591667924992"&gt;
&lt;p&gt;@&lt;a href="https://twitter.com/cdhowie"&gt;cdhowie&lt;/a&gt; what about how MSFT Devs everywhere ripped Google a new one for Dart but are embracing TypeScript with open arms. Hypocritical?&lt;/p&gt;
&amp;mdash; Kevin Darty (&lt;a href="https://twitter.com/kdarty"&gt;@kdarty&lt;/a&gt;) &lt;a href="https://twitter.com/kdarty/status/256948413537587200" data-datetime="2012-10-13T02:44:10+00:00"&gt;October 13, 2012&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;I can't speak for all MS devs, but I thought it worth sharing my opinion... I certainly LOL'd at the Hell World JavaScript created from Dart, but that was kinda funny (moreso with recent talk of Tree Shaking being a huge advantage of Dart). In general, however, I think Dart is a good idea, but possibly this effort could be better spent doing things slightly differently (more on that later).&lt;/p&gt;

&lt;p&gt;I think Dart is a &lt;em&gt;very&lt;/em&gt; ambitious project. I really hope it catches on and we see support in all browsers. However, I just can't see it happening :O( Dart and TypeScript solve some of the same issues but coming from the exact opposite sides.&lt;/p&gt;

&lt;p&gt;TypeScript is designed to be compatible with JavaScript so you can use all of the existing JS libraries out there, and slowly/progressively add TypeScript to your code.&lt;/p&gt;

&lt;p&gt;Dart goes further, and also provides a runtime, which brings huge performance benefits (where it can be executed natively). It compiles to JavaScript for compatibility (a necessity to ever gain traction). It tries to do so much more than TypeScript, however the trade-off is that it doesn't integrate with existing JS libraries.&lt;/p&gt;

&lt;p&gt;
In an ideal world, the decision to use something like TypeScript vs Dart should be based on things like:
&lt;ol&gt;
&lt;li&gt;Whether a Dart runtime is available to the majority of your users&lt;/li&gt;
&lt;li&gt;Whether you need to interact with a lot of existing JS libraries&lt;/li&gt;
&lt;li&gt;Whether you need to interact with a lot of your own JS (legacy projects)&lt;/li&gt;
&lt;li&gt;Whether you can invest the time required to learn Dart (there's a lot less to learn with TypeScript)&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;

&lt;p&gt;
However, I can't help but think that the decision is actually going to be based on one thing only:
&lt;ol&gt;
&lt;li&gt;Are you using Visual Studio or an Open Source IDE?&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;

&lt;p&gt;I just can't see TypeScript support ever being as good in non-VS IDEs. Nor can I see Dart support in VS ever being as good as it will elsewhere.&lt;/p&gt;

&lt;p&gt;I actually think we could do much better than Dart or TypeScript. &lt;a href="http://www.hanselman.com/"&gt;Scott Hanselman&lt;/a&gt; &lt;a href="http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx"&gt;described JavaScript as the "Assembly Language for the Web"&lt;/a&gt;. I think this is the first problem to address. We need something that runs in all browsers that addresses the issues with JavaScript, but it should be more like an intermediate language that other languages/tools can compile to (like we currently do with JavaScript, but less shit). That way, the choice of language can be decoupled from the browser support. If you want to write C#, VB.NET, Dart, Java, etc., it shouldn't matter. As long as they can all be compiled to a standard "IL", they should be deployable to a browser.&lt;/p&gt;

&lt;p&gt;I don't know whether any of the existing technologies are good candidates for this (could we compile C#/Java/etc. into Dart? NaCl?), but I think it would be the best solution for all. People could use the IDE and languages they feel most comfortable/productive with.&lt;/p&gt;

&lt;p&gt;In the meantime, compiling to JavaScript is the only thing we can do for compatibility. But in the background, I wish the standards bodies and browser vendors could get their heads together and agree on a new way of executing code in the browser in a way that they can all offer the experiences they wish to their devs. It doesn't matter whether it's based on something existing or new; the important thing is that it's not tied to the web development language and that the browser vendors and the companies with huge influences in development get behind it. &lt;strong&gt;If we can develop desktop apps and server-side code in the language of our choice; why can't we do the same for the browser?&lt;/strong&gt; This would allow huge amounts of code reuse across our applications that we don't currently get.&lt;/p&gt;

&lt;p&gt;Chrome already has NaCl, and will presumably have native Dart support soon. I wouldn't be surprised if IE ended up with native TypeScript support. This is going backwards, not forwards.&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2012/10/thoughts-on-typescript-dart-and-the-future-of-browser-based-coding/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/SuYlCdJ7qVk" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/SuYlCdJ7qVk/" title="Thoughts on TypeScript, Dart, and the future of browser-based coding" />
	<feedburner:origLink>https://blog.dantup.com/2012/10/thoughts-on-typescript-dart-and-the-future-of-browser-based-coding/</feedburner:origLink></entry>
	
	<entry>
		<id>tag:blog.dantup.com,2012-10-11:/2012/10/fixing-adb-device-not-found-with-nexus-7-in-recovery-mode/</id>
		<published>2012-10-11T00:00:00+00:00</published>
		<updated>2012-10-11T00:00:00+00:00</updated>
		
			<category term="Android" />
		
		<title type="text">Fixing adb "Device not found" with Nexus 7/Android devices in Recovery Mode</title>
		<content type="html" xml:base="https://blog.dantup.com">
			&lt;p&gt;I'm a huge fan of the &lt;a href="https://amzredir.com/?search=nexus%207&amp;tag=dtinfo"&gt;Nexus 7&lt;/a&gt;, but one of the things that annoyed me from day one was the lack of landscape support on the homescreen. I almost always use the tablet in landscape mode, so when switching to an app I didn't already have in the open/recent apps list, I would have to hit the Home button; which flipped everything around temporarily. There was already a user-set orientation lock, and we knew Android could handle it (from other devices, and those with rooted devices changing build properties), so it always seemed like a really random restriction.&lt;/p&gt;

&lt;p&gt;This was announced as fixed in Android 4.1.2, which began rolling out only a few days ago. As of this evening, my device still showed no update, so after finding &lt;a href="http://www.tapscape.com/want-to-upgrade-your-nexus-7-to-android-jelly-bean-4-1-2-heres-how/"&gt;some instructions&lt;/a&gt;, I decided to load the update myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These instructions do &lt;em&gt;*not*&lt;/em&gt; require you to root your device or unlock the bootloader&lt;/strong&gt;. If your device is unlocked/rooted, I have no idea what affect that might have on this process. If you're unsure about this, don't do it. I accept no liability if you mess things up. I have only limited knowledge of Android! The steps seemed fairly straight forward, which is the only reason I was attempting them!&lt;/p&gt;

&lt;p&gt;My first problem was that I was unable to boot into recovery mode using the instructions. When I selected Recovery Mode from the boot menu, my device just booted normally. I didn't get the Android with the red exclamation mark. This was easily fixed by using adb (installed as part of the Android SDK) and running:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-shell-session" data-lang="shell-session"&gt;&lt;span class="go"&gt;adb reboot recovery&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This rebooted the device to the screen showing an Android with a red exclamation mark, and pressing Power + Up allowed me to select the "Update from ADB" option.&lt;/p&gt;

&lt;p&gt;The problem was, once in this mode, Windows failed to recognise the decide/load the drivers that were previously loaded (before the reboot), which meant adb would no longer be able to connect the device:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-shell-session" data-lang="shell-session"&gt;&lt;span class="go"&gt;adb: device not found&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After a lot of searching online, I figured out that the problem was that the Hardware ID of my &lt;a href="https://amzredir.com/?search=nexus%207&amp;tag=dtinfo"&gt;Nexus 7&lt;/a&gt; when in recovery mode was not listed in "android_winusb.inf" file from the &lt;a href="http://developer.android.com/sdk/win-usb.html"&gt;Google Android Windows USB Drivers&lt;/a&gt;. The ID I saw in device manager against the Nexus was &lt;em&gt;USB\VID_18D1&amp;PID_D001&lt;/em&gt;, which didn't turn a lot up in Google!&lt;/p&gt;

&lt;p&gt;I decided to try adding this to the inf file manually (&lt;em&gt;C:\Program Files (x86)\Android\android-sdk\extras\google\usb_driver\android_winusb.inf&lt;/em&gt;). As I'm running on a 64bit machine, I added the following to the &lt;em&gt;[Google.NTamd64]&lt;/em&gt; section (note: this applies to Intel 64 bit machines, not just AMDs!). If you're using 32bit, you'd want to add it to the &lt;em&gt;[Google.NTx86]&lt;/em&gt; section:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="nn"&gt;[Google.NTamd64]&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;&lt;br data-jekyll-commonmark-ghpages&gt;&lt;span class="c"&gt;; !-- snip other devices... --!&lt;br data-jekyll-commonmark-ghpages&gt;&lt;/span&gt;&lt;br data-jekyll-commonmark-ghpages&gt;&lt;span class="c"&gt;; Google Nexus 7&lt;br data-jekyll-commonmark-ghpages&gt;; !-- snip existing device IDs --!&lt;br data-jekyll-commonmark-ghpages&gt;; This is the HardwareID shown in Device Manager for the "Nexus"&lt;br data-jekyll-commonmark-ghpages&gt;;  device when in Recovery mode&lt;br data-jekyll-commonmark-ghpages&gt;&lt;/span&gt;&lt;span class="err"&gt;%CompositeAdbInterface%&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;USB_Install,&lt;/span&gt; &lt;span class="err"&gt;USB\VID_18D1&amp;amp;PID_D001&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After making this change, I uninstalled the device drivers, then reinstalled them using this inf file. I once again rebooted into Recovery mode, and this time, Windows correctly displayed the device as "Android ADB Interface", and I was then able to sideload the Android 4.1.2 update (&lt;a href="http://android.clients.google.com/packages/ota/google_nakasi/03a4eaf95f73.signed-nakasi-JZO54K-from-JRO03D.03a4eaf9.zip"&gt;downloaded directly from Google&lt;/a&gt;) by typing:&lt;/p&gt;

&lt;figure class="highlight"&gt;&lt;pre&gt;&lt;code class="language-shell-session" data-lang="shell-session"&gt;&lt;span class="go"&gt;adb sideload 03a4eaf95f73.signed-nakasi-JZO54K-from-JRO03D.03a4eaf9.zip&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;ADB transmitted the file to the &lt;a href="https://amzredir.com/?search=nexus%207&amp;tag=dtinfo"&gt;Nexus 7&lt;/a&gt;, then the Nexus spat out the progress as it verified the image and updated the device. A reboot later, and my Nexus 7 was running Android 4.1.2!&lt;/p&gt;

			&lt;p&gt;This post was served up via my RSS feed. Please &lt;a href="https://blog.dantup.com/2012/10/fixing-adb-device-not-found-with-nexus-7-in-recovery-mode/"&gt;visit the original article&lt;/a&gt; to read/post comments. If you found this article interesting, why not &lt;a href="http://www.twitter.com/DanTup"&gt;follow @DanTup on Twitter&lt;/a&gt; for more? :)&lt;/p&gt;
		&lt;img src="http://feeds.feedburner.com/~r/DanTup/~4/sCATwWId3hA" height="1" width="1" alt=""/&gt;</content>
		<link rel="alternate" type="text/html" href="http://feeds.dantup.com/~r/DanTup/~3/sCATwWId3hA/" title="Fixing adb &quot;Device not found&quot; with Nexus 7/Android devices in Recovery Mode" />
	<feedburner:origLink>https://blog.dantup.com/2012/10/fixing-adb-device-not-found-with-nexus-7-in-recovery-mode/</feedburner:origLink></entry>
	
</feed>
