Deleting Windows run history

If you have butter fingers like me, and over time end up with a lot of old commands with typos in your Windows run box that get annoying – deleting them is a simple. All you need to do it remove the following registry key.

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU

Now every time one plays with regedit, it can be dangerous – you can also save this commend as a .cmd file, and then run it with admin privileges – essentially does the same thing.

reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU" /f

You can also download the same thing from here.

Docker / Docker Compose on a Pi

Been playing with a few things at home, and as part of that was trying to get Docker and Docker Compose running on a Raspberry Pi. Docker Compose if you aren’t familiar with, allows one to run multi-container apps, and is very handy when building multi-tier layered applications – which are quite common.

I was running it docker on my (Synology) NAS, but a recent update from them broke docker – specifically environment variables. That in turn broke the ability to run Docker Compose, and of course a bunch of stuff; and the opportunity to experiment.

First, we need to install docker – which these days is quite simple. You need the ability to ssh into the pi (or if you are connected to a display, then via a terminal prompt). And in some cases if things fail then you might need to run them as root (via sudo). To install docker, run the following:

curl -sSL https://get.docker.com | sh

And once you are done installing docker, then test it by running the classic hello world image. To so that you run the following command – this will get the Hello World image, and once run will automatically remove it (which is because of the –rm option)

docker run --rm hello-world

If everything is installed OK, then you should see a output that looks something like this the shown below. And this is good – means everything is up and running as expected.

pi@pi-server2:~ $ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1eda109e4da: Pull complete
Digest: sha256:b8ba256769a0ac28dd126d584e0a2011cd2877f3f76e093a7ae560f2a5301c00
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm32v7)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

To make like more simple, you should add the user you are logged in as to the ‘docker’ group. In my case it is the default ‘pi’ user, so that command would look like this. And for this to take effect, you would need to logout – I just reboot the machine – old habits. 🙂

sudo usermod -aG docker pi

OK, now that docker is installed, lets get to docker-compose. For that we first install pip, and use that to install docker-compose. And don’t forget the apt-get update in the end.

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && sudo python3 get-pip.py
sudo pip3 install docker-compose
sudo apt-get update

Now before anything else, lets try and make sure all dependencies are there. Create a file called ‘docker-compose.yml’ with the following. You can put this file anywhere, but I like to create a separate folder and save it in that.

In this example I expose port 6666 to the host which is mapped to port 8000 internally on the image. If your port 6666 is taken you can choose another port – it doesn’t matter. Spacing and indent, do matter in a yml file, so you would want to pay extra attention to that.

version: '3'
services:
  webapp:
    ports:
      - 6666:8000
    image: python:3.7-alpine
    command: "python -m http.server 8000"

Once the file is saved you run it with the following command. The image handles you would see are very likely going to be different and that is OK.

pi@pi-server2:~/docker/docker-test $ docker-compose up
Creating network "docker-test_default" with the default driver
Pulling webapp (python:3.7-alpine)...
3.7-alpine: Pulling from library/python
33b18ff7f9b7: Pull complete
0c1f90421c3a: Pull complete
91543a0ba590: Pull complete
913b1310b79e: Pull complete
6b545e90ee55: Pull complete                                                                                             Digest: sha256:9363cb46e52894a22ba87ebec0845d30f4c27efd6b907705ba9a27192b45e797
Status: Downloaded newer image for python:3.7-alpine
Creating docker-test_webapp_1 ... done                                                                                  Attaching to docker-test_webapp_1

At this point, the image is running in attached mode and it seems like it is waiting, when in reality it is running. If you open another ssh terminal and type in the following command – change the port to whatever you used earlier in the yml file.

pi@pi-server2:~ $ curl -iv 0.0.0.0:6666

And if everything is working then you will see a output something like this. And if you see towards the top you got a HTTP 200 – that is all that mattes in this case.

* Expire in 0 ms for 6 (transfer 0x1b097c0)
*   Trying 0.0.0.0...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x1b097c0)
* Connected to 0.0.0.0 (127.0.0.1) port 6666 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:6666
> User-Agent: curl/7.64.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.7.4
Server: SimpleHTTP/0.6 Python/3.7.4
< Date: Thu, 26 Sep 2019 22:10:28 GMT
Date: Thu, 26 Sep 2019 22:10:28 GMT
< Content-type: text/html; charset=utf-8
Content-type: text/html; charset=utf-8
< Content-Length: 915
Content-Length: 915

<
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href=".dockerenv">.dockerenv</a></li>
<li><a href="bin/">bin/</a></li>
<li><a href="dev/">dev/</a></li>
<li><a href="etc/">etc/</a></li>
<li><a href="home/">home/</a></li>
<li><a href="lib/">lib/</a></li>
<li><a href="media/">media/</a></li>
<li><a href="mnt/">mnt/</a></li>
<li><a href="opt/">opt/</a></li>
<li><a href="proc/">proc/</a></li>
<li><a href="root/">root/</a></li>
<li><a href="run/">run/</a></li>
<li><a href="sbin/">sbin/</a></li>
<li><a href="srv/">srv/</a></li>
<li><a href="sys/">sys/</a></li>
<li><a href="tmp/">tmp/</a></li>
<li><a href="usr/">usr/</a></li>
<li><a href="var/">var/</a></li>
</ul>
<hr>
</body>
</html>
* Closing connection 0

You can go back to the first ssh session and hit Ctrl + C to shutdown the image. Once you do that you will see something like:

^CGracefully stopping... (press Ctrl+C again to force)
Stopping docker-test_webapp_1 ... done                                                                                  pi@pi-server2:~/docker/docker-test $

Now you know docker-compose and all the dependencies are installed. Next I would want docker to auto start whenever the pi boots up, and for that we will use the following two commands.

sudo systemctl enable docker
sudo systemctl start docker

And that should be it. If you are running low on space you might want to clean up the images we downloaded in testing this installation.

Tesla API v3.9.1

Haven’t had time until now to explore on what is new as Tesla continues to push updates. The latest version as of this post is v3.9.1 which is what there I decompiled and when compared to the earlier version (I had posted (v3.8.2), there three new REST API’s outlined below.

Service data from the car – not sure what exactly does this will. Need to try it.

   "VEHICLE_SERVICE_DATA": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/service_data",
    "AUTH": true
  }

Now, when I call that, I get a 200OK response (see below), so it is accepting the request, and that includes the bearer code in the header as expected. I don’t see anything interesting back, but that could be because my car is not in service. Maybe someone who has their vehicle in the service center can try and validate this.

{
    "response": {}
}

The next new API is a POST, for reports; and calling this just sends a 200OK back, but I don’t know what it is for. It seems very similar to the SEND_LOG method.

"SEND_REPORT": {
    "TYPE": "POST",
    "URI": "api/1/reports",
    "AUTH": true
  }

The next two set of APIs seem quite interesting and related t AutoPilot upgrade. It might be that these could be in app purchases – checking the eligibility, and then allowing one to purchase.

"UPGRADE_ELIGIBILITY": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/eligible_upgrades",
    "AUTH": true
  },
  "AUTOPILOT_UPGRADE_URL": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/purchase_url",
    "AUTH": true
  }

When I try and call the Purchase_URL, I get a HTTP 400, and seems like I am missing some parameters – other than the headers.

{
    "error": "bad_request",
    "error_description": "The data given to this server does not meet our criteria."
}

And calling the eligible_upgrades I get a ‘false’. Now I already have AutoPilot, so this might make sense. And given this seems to be a key-value pair, I am guessing there will be other things that Tesla would add over time to up-sell.

{
    "autopilot": false
}

The final new API is related to energy sites, and something I of course don’t have or have an interest, but sharing here if someone does care. 🙂

"CALENDAR_HISTORY_DATA": {
    "TYPE": "GET",
    "URI": "api/1/energy_sites/{site_id}/calendar_history",
    "AUTH": true
  }

I am not publishing the full API here as there aren’t significant changes. You of course can see the older post which has the details.

npm install blues – npm ERR! Error: Method Not Allowed

This is a output of a few frustrating hours (spanning over a few days – as and when I can get time), and finally got it fixed and working. Hopefully it might help someone who is also dealing with npm blues.

When NodeJS and npm works, its awesome. But when it borks, it is worst than my code or so it seems :).

Been playing with a few things and wanting to get a dashboard going with Grafana (and InfluxBD as a time-series DB). But some of the installation was failing and for the life of me, could not figure out why and how. Clean image install and downgrading to the previous stable version also didn’t help.

One example of npm failing miserably was the “Error: Method not Allowed” which is not very helpful. Here is an example of what I was seeing:

root@pi-server:/var/lib/grafana/plugins/grafana-trackmap-panel# npm install
(node:4538) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
npm ERR! Error: Method Not Allowed
npm ERR!     at errorResponse (/usr/share/npm/lib/cache/add-named.js:260:10)
npm ERR!     at /usr/share/npm/lib/cache/add-named.js:203:12
npm ERR!     at saved (/usr/share/npm/node_modules/npm-registry-client/lib/get.js:167:7)
npm ERR!     at FSReqWrap.oncomplete (fs.js:135:15)
npm ERR! If you need help, you may report this *entire* log,
npm ERR! including the npm and node versions, at:
npm ERR!     <http://github.com/npm/npm/issues>

npm ERR! System Linux 4.19.57-v7+
npm ERR! command "/usr/bin/node" "/usr/bin/npm" "install"
npm ERR! cwd /var/lib/grafana/plugins/grafana-trackmap-panel
npm ERR! node -v v8.11.1
npm ERR! npm -v 1.4.21
npm ERR! code E405
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     /var/lib/grafana/plugins/grafana-trackmap-panel/npm-debug.log
npm ERR! not ok code 0
root@pi-server:/var/lib/grafana/plugins/grafana-trackmap-panel#

Again, like I said not very helpful. But I finally got to be able to fix it and move on. And here is what worked for me, and it seems like in the OS image, there was a corrupted files, at some level. In most cases you need root access.

Step 1: – Remove and clean up NodeJS.

sudo apt-get remove nodejs nodejs-legacy nodered

Step 2: Get the latest stable source.

curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g npm@latest\

I also noticed sometimes the commands above don’t work. If that is the case then then try the following, to get the latest.

curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g npm@latest

And based on your dependencies, v9 might not work and you need v8 then you change the first line as following Or for the latest:

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g npm@latest

And finally in the end install and start.

npm install &amp;&amp; npm start

And if you do need to check for the update and get the latest, then try:

sudo npm install -g npm@latest

ML Algorithms

Sometimes one needs a quick snapshot of what are the options to think through and I really like this for that.

Machine Learning Algorithms
Machine Learning Algorithms

Geek Haiku 3 – Streaming Chaos

Rain drops as I dive,
into packet stream; Chaos.
Malicious patterns.

#Haiku #GeekHaiku

Atom

Never trust an atom, they make up everything. 🤓

#GeekyJokes

Programming

A key virtue of a programmer is laziness. As an example it is what inspires me to automate my home to the point where I don’t have to lift a finger to switch on the light. Removing friction from a system is a anesthetic joy. The drug of efficiency, feels really good.

I still write code and people get surprised by that sometimes – maybe it’s the quality of the code 🤓.

Tesla REST APIs v3.8.2

It has been a while since I played with the various Tesla endpoint (APIs) – been too busy and haven’t had the time. I de-compiled the Tesla app and noticed a few new things in there – or at least new to me.

The following are the ones which seem new and stand out. How exactly some of these are used, can only be one’s guess, but I can certainly infer a few things from this.

  • VEHICLE_DATA_LEGACY – So this seems to be the ‘old’ end point, hence the legacy. The new endpoint is now at ‘VEHICLE_DATA’ which seems to return a combined (some) vehicle information, and, consolidate data state of the vehicle. This seems to be cleaner than the earlier version where it was too isolated and multiple calls.
  • NEARBY_CHARGING_SITES – The name says it all – returns a list of Tesla chargers close by (both superchargers, and destination chargers).
  • Media – there are a few media controls that are outlined below. I think these were part of earlier updates when a passenger could control the media playback from their phone. Most of the names are self explanatory and I skipped outlining them below.
    • MEDIA_NEXT_TRACK and MEDIA_PREVIOUS_TRACK – plays the next and previous track respectively.
    • MEDIA_NEXT_FAVORITE and MEDIA_PREVIOUS_FAVORITE – This skips to the next / previous favourite station (different from the track).
  • DEACTIVATE_DEVICE_TOKEN – This is new but I am not sure how this is different from REVOKE_AUTH_TOKEN. What kind of devices is this looking to revoke? AFAIK, it doesn’t seem to be related to the Powerwall.
  • ROADSIDE_ASSISTANCE_DATA – Intrigued seeing this and not sure what data it is sending (need to spend more time writing code to examine the output of the API). I wonder if this is related to the ETA details that might be pushed out (see Elon’s tweet).
  • SET_SENTRY_MODE – As the name suggests, this toggles Sentry mode for the car.
  • Software updates (from the phone) – as expected a couple of API’s to start and cancel software updates – SCHEDULE_SOFTWARE_UPDATE, CANCEL_SOFTWARE_UPDATE
  • REMOTE_SEAT_HEATER_REQUEST – Switching on the seat heating in the car. I presume there will be parameters on which seat, and the setting for each of the seats.
  • REFERRAL_DATA – I would be interested to see what this shows and how it is changing on the backend given that Tesla can’t seem to make up their mind on how to run this and keep changing it adhoc.
  • Message Center – there are a bunch of API’s that are around message center and I wonder what that exactly means. Is it messages in the app (you know, the Inbox that you have seen), or is it something new coming out on the screen in the car. (e.g. MESSAGE_CENTER_MESSAGE, MESSAGE_CENTER_MESSAGE_ACTION_UPDATE, etc.

I have the full output pasted below for you to have a look . This is as of v3.8.2 and it includes not just the car, but the powerwall, and the charging sites (both destination and superchargers).

{
  "AUTHENTICATE": {
    "TYPE": "POST",
    "URI": "oauth/token",
    "AUTH": false
  },
  "REVOKE_AUTH_TOKEN": {
    "TYPE": "POST",
    "URI": "oauth/revoke",
    "AUTH": true
  },
  "PRODUCT_LIST": {
    "TYPE": "GET",
    "URI": "api/1/products",
    "AUTH": true
  },
  "VEHICLE_LIST": {
    "TYPE": "GET",
    "URI": "api/1/vehicles",
    "AUTH": true
  },
  "VEHICLE_SUMMARY": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}",
    "AUTH": true
  },
  "VEHICLE_DATA_LEGACY": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/data",
    "AUTH": true
  },
  "VEHICLE_DATA": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/vehicle_data",
    "AUTH": true
  },
  "NEARBY_CHARGING_SITES": {
    "TYPE": "GET",
    "URI": "api/1/vehicles/{vehicle_id}/nearby_charging_sites",
    "AUTH": true
  },
  "WAKE_UP": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/wake_up",
    "AUTH": true
  },
  "UNLOCK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/door_unlock",
    "AUTH": true
  },
  "LOCK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/door_lock",
    "AUTH": true
  },
  "HONK_HORN": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/honk_horn",
    "AUTH": true
  },
  "FLASH_LIGHTS": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/flash_lights",
    "AUTH": true
  },
  "CLIMATE_ON": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/auto_conditioning_start",
    "AUTH": true
  },
  "CLIMATE_OFF": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/auto_conditioning_stop",
    "AUTH": true
  },
  "CHANGE_CLIMATE_TEMPERATURE_SETTING": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/set_temps",
    "AUTH": true
  },
  "CHANGE_CHARGE_LIMIT": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/set_charge_limit",
    "AUTH": true
  },
  "CHANGE_SUNROOF_STATE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/sun_roof_control",
    "AUTH": true
  },
  "ACTUATE_TRUNK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/actuate_trunk",
    "AUTH": true
  },
  "REMOTE_START": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/remote_start_drive",
    "AUTH": true
  },
  "CHARGE_PORT_DOOR_OPEN": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/charge_port_door_open",
    "AUTH": true
  },
  "CHARGE_PORT_DOOR_CLOSE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/charge_port_door_close",
    "AUTH": true
  },
  "START_CHARGE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/charge_start",
    "AUTH": true
  },
  "STOP_CHARGE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/charge_stop",
    "AUTH": true
  },
  "MEDIA_TOGGLE_PLAYBACK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_toggle_playback",
    "AUTH": true
  },
  "MEDIA_NEXT_TRACK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_next_track",
    "AUTH": true
  },
  "MEDIA_PREVIOUS_TRACK": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_prev_track",
    "AUTH": true
  },
  "MEDIA_NEXT_FAVORITE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_next_fav",
    "AUTH": true
  },
  "MEDIA_PREVIOUS_FAVORITE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_prev_fav",
    "AUTH": true
  },
  "MEDIA_VOLUME_UP": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_volume_up",
    "AUTH": true
  },
  "MEDIA_VOLUME_DOWN": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/media_volume_down",
    "AUTH": true
  },
  "SEND_LOG": {
    "TYPE": "POST",
    "URI": "api/1/logs",
    "AUTH": true
  },
  "RETRIEVE_NOTIFICATION_PREFERENCES": {
    "TYPE": "GET",
    "URI": "api/1/notification_preferences",
    "AUTH": true
  },
  "SEND_NOTIFICATION_PREFERENCES": {
    "TYPE": "POST",
    "URI": "api/1/notification_preferences",
    "AUTH": true
  },
  "RETRIEVE_NOTIFICATION_SUBSCRIPTION_PREFERENCES": {
    "TYPE": "GET",
    "URI": "api/1/vehicle_subscriptions",
    "AUTH": true
  },
  "SEND_NOTIFICATION_SUBSCRIPTION_PREFERENCES": {
    "TYPE": "POST",
    "URI": "api/1/vehicle_subscriptions",
    "AUTH": true
  },
  "DEACTIVATE_DEVICE_TOKEN": {
    "TYPE": "POST",
    "URI": "api/1/device/{device_token}/deactivate",
    "AUTH": true
  },
  "CALENDAR_SYNC": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/upcoming_calendar_entries",
    "AUTH": true
  },
  "SET_VALET_MODE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/set_valet_mode",
    "AUTH": true
  },
  "RESET_VALET_PIN": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/reset_valet_pin",
    "AUTH": true
  },
  "SPEED_LIMIT_ACTIVATE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/speed_limit_activate",
    "AUTH": true
  },
  "SPEED_LIMIT_DEACTIVATE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/speed_limit_deactivate",
    "AUTH": true
  },
  "SPEED_LIMIT_SET_LIMIT": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/speed_limit_set_limit",
    "AUTH": true
  },
  "SPEED_LIMIT_CLEAR_PIN": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/speed_limit_clear_pin",
    "AUTH": true
  },
  "SCHEDULE_SOFTWARE_UPDATE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/schedule_software_update",
    "AUTH": true
  },
  "CANCEL_SOFTWARE_UPDATE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/cancel_software_update",
    "AUTH": true
  },
  "SET_SENTRY_MODE": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/set_sentry_mode",
    "AUTH": true
  },
  "POWERWALL_ORDER_SESSION_DATA": {
    "TYPE": "GET",
    "URI": "api/1/users/powerwall_order_entry_data",
    "AUTH": true
  },
  "POWERWALL_ORDER_PAGE": {
    "TYPE": "GET",
    "URI": "powerwall_order_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "ONBOARDING_EXPERIENCE": {
    "TYPE": "GET",
    "URI": "api/1/users/onboarding_data",
    "AUTH": true
  },
  "ONBOARDING_EXPERIENCE_PAGE": {
    "TYPE": "GET",
    "URI": "onboarding_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "SERVICE_SELF_SCHEDULING_ELIGIBILITY": {
    "TYPE": "GET",
    "URI": "api/1/users/service_scheduling_data",
    "AUTH": true
  },
  "SERVICE_SELF_SCHEDULING_PAGE": {
    "TYPE": "GET",
    "URI": "service_scheduling_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "REFERRAL_DATA": {
    "TYPE": "GET",
    "URI": "api/1/users/referral_data",
    "AUTH": true
  },
  "REFERRAL_PAGE": {
    "TYPE": "GET",
    "URI": "referral_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "ROADSIDE_ASSISTANCE_DATA": {
    "TYPE": "GET",
    "URI": "api/1/users/roadside_assistance_data",
    "AUTH": true
  },
  "ROADSIDE_ASSISTANCE_PAGE": {
    "TYPE": "GET",
    "URI": "roadside_assistance_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "MESSAGE_CENTER_MESSAGE_LIST": {
    "TYPE": "GET",
    "URI": "api/1/messages",
    "AUTH": true
  },
  "MESSAGE_CENTER_MESSAGE": {
    "TYPE": "GET",
    "URI": "api/1/messages/{message_id}",
    "AUTH": true
  },
  "MESSAGE_CENTER_COUNTS": {
    "TYPE": "GET",
    "URI": "api/1/messages/count",
    "AUTH": true
  },
  "MESSAGE_CENTER_MESSAGE_ACTION_UPDATE": {
    "TYPE": "POST",
    "URI": "api/1/messages/{message_id}/actions",
    "AUTH": true
  },
  "MESSAGE_CENTER_CTA_PAGE": {
    "TYPE": "GET",
    "URI": "messages_cta_page",
    "AUTH": true,
    "CONTENT": "HTML"
  },
  "AUTH_COMMAND_TOKEN": {
    "TYPE": "POST",
    "URI": "api/1/users/command_token",
    "AUTH": true
  },
  "SEND_DEVICE_KEY": {
    "TYPE": "POST",
    "URI": "api/1/users/keys",
    "AUTH": true
  },
  "DIAGNOSTICS_ENTITLEMENTS": {
    "TYPE": "GET",
    "URI": "api/1/diagnostics",
    "AUTH": true
  },
  "SEND_DIAGNOSTICS": {
    "TYPE": "POST",
    "URI": "api/1/diagnostics",
    "AUTH": true
  },
  "BATTERY_SUMMARY": {
    "TYPE": "GET",
    "URI": "api/1/powerwalls/{battery_id}/status",
    "AUTH": true
  },
  "BATTERY_DATA": {
    "TYPE": "GET",
    "URI": "api/1/powerwalls/{battery_id}",
    "AUTH": true
  },
  "BATTERY_POWER_TIMESERIES_DATA": {
    "TYPE": "GET",
    "URI": "api/1/powerwalls/{battery_id}/powerhistory",
    "AUTH": true
  },
  "BATTERY_ENERGY_TIMESERIES_DATA": {
    "TYPE": "GET",
    "URI": "api/1/powerwalls/{battery_id}/energyhistory",
    "AUTH": true
  },
  "BATTERY_BACKUP_RESERVE": {
    "TYPE": "POST",
    "URI": "api/1/powerwalls/{battery_id}/backup",
    "AUTH": true
  },
  "BATTERY_SITE_NAME": {
    "TYPE": "POST",
    "URI": "api/1/powerwalls/{battery_id}/site_name",
    "AUTH": true
  },
  "BATTERY_OPERATION_MODE": {
    "TYPE": "POST",
    "URI": "api/1/powerwalls/{battery_id}/operation",
    "AUTH": true
  },
  "SITE_SUMMARY": {
    "TYPE": "GET",
    "URI": "api/1/energy_sites/{site_id}/status",
    "AUTH": true
  },
  "SITE_DATA": {
    "TYPE": "GET",
    "URI": "api/1/energy_sites/{site_id}/live_status",
    "AUTH": true
  },
  "SITE_CONFIG": {
    "TYPE": "GET",
    "URI": "api/1/energy_sites/{site_id}/site_info",
    "AUTH": true
  },
  "HISTORY_DATA": {
    "TYPE": "GET",
    "URI": "api/1/energy_sites/{site_id}/history",
    "AUTH": true
  },
  "BACKUP_RESERVE": {
    "TYPE": "POST",
    "URI": "api/1/energy_sites/{site_id}/backup",
    "AUTH": true
  },
  "SITE_NAME": {
    "TYPE": "POST",
    "URI": "api/1/energy_sites/{site_id}/site_name",
    "AUTH": true
  },
  "OPERATION_MODE": {
    "TYPE": "POST",
    "URI": "api/1/energy_sites/{site_id}/operation",
    "AUTH": true
  },
  "TIME_OF_USE_SETTINGS": {
    "TYPE": "POST",
    "URI": "api/1/energy_sites/{site_id}/time_of_use_settings",
    "AUTH": true
  },
  "STORM_MODE_SETTINGS": {
    "TYPE": "POST",
    "URI": "api/1/energy_sites/{site_id}/storm_mode",
    "AUTH": true
  },
  "SEND_NOTIFICATION_CONFIRMATION": {
    "TYPE": "POST",
    "URI": "api/1/notification_confirmations",
    "AUTH": true
  },
  "NAVIGATION_REQUEST": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/navigation_request",
    "AUTH": true
  },
  "REMOTE_SEAT_HEATER_REQUEST": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/remote_seat_heater_request",
    "AUTH": true
  },
  "REMOTE_STEERING_WHEEL_HEATER_REQUEST": {
    "TYPE": "POST",
    "URI": "api/1/vehicles/{vehicle_id}/command/remote_steering_wheel_heater_request",
    "AUTH": true
  }
}

Getting DonkeyCar working on a Mac

I have been playing with a #selfdriving car for a while, and that is super exciting. From a #AI and #ML perspective it is small scale, but allows one to exploit all aspects of the tech stack and also appreciate the limitations of not only the software, but also the hardware.

With this You run a NN on a raspberry pi that uses TensorFlow, and Keras and runs inference on the edge. The pi doesn’t have enough power to train, so you need to do that on a beefier machine and then deploy the model back to run this.

Now, I didn’t have any issues in getting this running on Windows, but to get it on a Mac was a different story. The documentation is there that outlines all the steps, and even if you follow it to the T, it breaks right in the end.

When I tried to create a car, using a createcar command (this essentially creates the buckets, where you would save the training images, and the model, and the configuration of the car when you connect to it from your machine). The actual file paths would probably be different for you but, essentially it is the same thing.

(donkey) AMAC02XN1T9JGH5:donkeycar amit.bahree$ donkey createcar ~/mycar
Traceback (most recent call last):
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 660, in _build_master
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 968, in require
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 859, in resolve
pkg_resources.ContextualVersionConflict: (imageio 2.4.1 (/anaconda3/envs/donkey/lib/python3.6/site-packages), Requirement.parse('imageio<3.0,>=2.5'), {'moviepy'})

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/anaconda3/envs/donkey/bin/donkey", line 6, in <module>
    from pkg_resources import load_entry_point
  File "<frozen importlib._bootstrap>", line 961, in _find_and_load
  File "<frozen importlib._bootstrap>", line 950, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 646, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 616, in _load_backward_compatible
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 2985, in <module>
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 2971, in _call_aside
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 2998, in _initialize_master_working_set
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 662, in _build_master
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 675, in _build_from_requirements
  File "/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg_resources/__init__.py", line 854, in resolve
pkg_resources.DistributionNotFound: The 'imageio<3.0,>=2.5' distribution was not found and is required by moviepy

The key here to focus is on the last lines on both of those blocks of code – the main thing causing the issue is MoviePy (see highlighted lines above).

MoviePy is a Python library for video editing: cutting, concatenations, title insertions, video compositing (a.k.a. non-linear editing), video processing, and creation of custom effects.

It seems like when you go through the steps – clone the repo, setup anaconda, install tensorflow and get the car configured – there is a mismatch in the MoviePy dependencies which it doesn’t like. The way to fix the issue is outlined below.

Skip MoviePy

MoviePy is something you don’t need to use right away but later when trying to make a movie (using the makemovie command – which allows you to create a movie file from the images in a Tub.); this is not essential. To do this, the easiest way is to remove (or my suggestion it to comment) out the moviepy dependency from the setup.py file.

This should be line 33 in the setup.py file that you will find in the same folder where you cloned the git repo. As an example the updated file is below, where the moviepy dependency is commented out (see highlighted). And once you save this and go about creating the car, it should work. Of course you cannot use the makemovie option later.

from setuptools import setup, find_packages

import os

with open("README.md", "r") as fh:
    long_description = fh.read()


setup(name='donkeycar',
      version='2.5.7',
      description='Self driving library for python.',
      long_description=long_description,
      long_description_content_type="text/markdown",
      url='https://github.com/autorope/donkeycar',
      download_url='https://github.com/autorope/donkeycar/archive/2.1.5.tar.gz',
      author='Will Roscoe',
      author_email='wroscoe@gmail.com',
      license='MIT',
      entry_points={
          'console_scripts': [
              'donkey=donkeycar.management.base:execute_from_command_line',
          ],
      },
      install_requires=['numpy',
                        'pillow',
                        'docopt',
                        'tornado==4.5.3',
                        'requests',
                        'h5py',
                        'python-socketio',
                        'flask',
                        'eventlet',
                        #'moviepy',
                        'pandas',
                        ],

      extras_require={
                      'tf': ['tensorflow>=1.9.0'],
                      'tf_gpu': ['tensorflow-gpu>=1.9.0'],
                      'pi': [
                          'picamera',
                          'Adafruit_PCA9685',
                          ],
                      'dev': [
                          'pytest',
                          'pytest-cov',
                          'responses'
                          ],
                      'ci': ['codecov']
                  },

      include_package_data=True,

      classifiers=[
          # How mature is this project? Common values are
          #   3 - Alpha
          #   4 - Beta
          #   5 - Production/Stable
          'Development Status :: 3 - Alpha',

          # Indicate who your project is intended for
          'Intended Audience :: Developers',
          'Topic :: Scientific/Engineering :: Artificial Intelligence',

          # Pick your license as you wish (should match "license" above)
          'License :: OSI Approved :: MIT License',

          # Specify the Python versions you support here. In particular, ensure
          # that you indicate whether you support Python 2, Python 3 or both.

          'Programming Language :: Python :: 3.5',
          'Programming Language :: Python :: 3.6',
      ],
      keywords='selfdriving cars donkeycar diyrobocars',

      packages=find_packages(exclude=(['tests', 'docs', 'site', 'env'])),
      )

Once you have saved the setup.py file, you need to run the installation again with the following command and then run the create car command. Both of these are outlined below.

pip install -e .
donkey createcar ~/mycar

Once you run these, then you should see the successful installation as shown by the output below. Note – your output might be a little different depending on the conda state of packages

(donkey) AMAC02XN1T9JGH5:donkeycar amit.bahree$ pip install -e .
Obtaining file:///Users/amit.bahree/CloudStation/Documents/Code/donkeycar
Requirement already satisfied: numpy in /anaconda3/envs/donkey/lib/python3.6/site-packages (from donkeycar==2.5.7) (1.14.5)
Requirement already satisfied: pillow in /anaconda3/envs/donkey/lib/python3.6/site-packages (from donkeycar==2.5.7) (4.2.1)
Requirement already satisfied: docopt in /anaconda3/envs/donkey/lib/python3.6/site-packages (from donkeycar==2.5.7) (0.6.2)
Collecting tornado==4.5.3 (from donkeycar==2.5.7)
Requirement already satisfied: requests in /anaconda3/envs/donkey/lib/python3.6/site-packages (from donkeycar==2.5.7) (2.18.4)
Requirement already satisfied: h5py in /anaconda3/envs/donkey/lib/python3.6/site-packages (from donkeycar==2.5.7) (2.7.1)
Collecting python-socketio (from donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/a1/71/118e4b7fb453d7095d6863f4b783dbaa57109af4bc2380300649c8942d61/python_socketio-4.0.0-py2.py3-none-any.whl
Collecting flask (from donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl
Collecting eventlet (from donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/86/7e/96e1412f96eeb2f2eca9342dcc4d5bc9305880a448b603b0a8e54439b71c/eventlet-0.24.1-py2.py3-none-any.whl
Collecting pandas (from donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/99/12/bf4c58eea94cea4f91ff931f284146337814fb8546e6eb0b52584446fd52/pandas-0.24.1-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl
Requirement already satisfied: olefile in /anaconda3/envs/donkey/lib/python3.6/site-packages (from pillow->donkeycar==2.5.7) (0.44)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /anaconda3/envs/donkey/lib/python3.6/site-packages (from requests->donkeycar==2.5.7) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in /anaconda3/envs/donkey/lib/python3.6/site-packages (from requests->donkeycar==2.5.7) (2017.7.27.1)
Requirement already satisfied: idna<2.7,>=2.5 in /anaconda3/envs/donkey/lib/python3.6/site-packages (from requests->donkeycar==2.5.7) (2.6)
Requirement already satisfied: urllib3<1.23,>=1.21.1 in /anaconda3/envs/donkey/lib/python3.6/site-packages (from requests->donkeycar==2.5.7) (1.22)
Requirement already satisfied: six in /anaconda3/envs/donkey/lib/python3.6/site-packages (from h5py->donkeycar==2.5.7) (1.10.0)
Collecting python-engineio>=3.2.0 (from python-socketio->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/95/91/d083bd7b5d408af53633377dfbf87bf181236c8916d36213388b12eaa999/python_engineio-3.4.3-py2.py3-none-any.whl
Collecting click>=5.1 (from flask->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl
Collecting itsdangerous>=0.24 (from flask->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting Werkzeug>=0.14 (from flask->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl
Collecting Jinja2>=2.10 (from flask->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl
Collecting monotonic>=1.4 (from eventlet->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/ac/aa/063eca6a416f397bd99552c534c6d11d57f58f2e94c14780f3bbf818c4cf/monotonic-1.5-py2.py3-none-any.whl
Collecting greenlet>=0.3 (from eventlet->donkeycar==2.5.7)
Collecting dnspython>=1.15.0 (from eventlet->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/ec/d3/3aa0e7213ef72b8585747aa0e271a9523e713813b9a20177ebe1e939deb0/dnspython-1.16.0-py2.py3-none-any.whl
Collecting pytz>=2011k (from pandas->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/61/28/1d3920e4d1d50b19bc5d24398a7cd85cc7b9a75a490570d5a30c57622d34/pytz-2018.9-py2.py3-none-any.whl
Collecting python-dateutil>=2.5.0 (from pandas->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask->donkeycar==2.5.7)
  Using cached https://files.pythonhosted.org/packages/f0/00/a6aea33f5598b080b86d6b6d1214b51afe3ffa6100b902d5aa465080083f/MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl
Installing collected packages: tornado, python-engineio, python-socketio, click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, flask, monotonic, greenlet, dnspython, eventlet, pytz, python-dateutil, pandas, donkeycar
  Found existing installation: tornado 4.5.1
    Uninstalling tornado-4.5.1:
      Successfully uninstalled tornado-4.5.1
  Found existing installation: Werkzeug 0.12.2
    Uninstalling Werkzeug-0.12.2:
      Successfully uninstalled Werkzeug-0.12.2
  Running setup.py develop for donkeycar
Successfully installed Jinja2-2.10 MarkupSafe-1.1.1 Werkzeug-0.14.1 click-7.0 dnspython-1.16.0 donkeycar eventlet-0.24.1 flask-1.0.2 greenlet-0.4.15 itsdangerous-1.1.0 monotonic-1.5 pandas-0.24.1 python-dateutil-2.8.0 python-engineio-3.4.3 python-socketio-4.0.0 pytz-2018.9 tornado-4.5.3

And when I run the createcar, you can see it worked as expected. In my case creating the ‘mycar’ folder in my home directory. Of course you can choose this wherever you prefer.

(donkey) AMAC02XN1T9JGH5:donkeycar amit.bahree$ donkey createcar ~/mycar
using donkey version: 2.5.7 ...
Creating car folder: /Users/amit.bahree/mycar
making dir  /Users/amit.bahree/mycar
Creating data &amp; model folders.
making dir  /Users/amit.bahree/mycar/models
making dir  /Users/amit.bahree/mycar/data
making dir  /Users/amit.bahree/mycar/logs
Copying car application template: donkey2
Copying car config defaults. Adjust these before starting your car.
Donkey setup complete.

It is interesting to see this is more stable on Windows, than on a Mac. Also, one last thing to leave you with – when I first ran the installation, the hint that someone was wrong was in the output, but I didn’t pay too much attention to it. See the red line highlighted in the output below.

moviepy failure - donkeycar installation
moviepy failure – donkeycar installation

Don’t know at this time on what the solution for moviepy is to get this sorted – luckily its not a big deal at the moment.

threads

Some people, when confronted with a problem, think, ‘I know, I’ll use threads’ – and then two they hav erpoblesms.

#GeekyJokes and if you don’t get it, see this. 🙂

VSCode + Python on a mac

As my experimentation continues, I wanted to get Visual Studio Code installed on a mac, and wanted to use python as the language of choice – main reason for the mac is to understand and explore the #ML libraries, runtimes, and their support on a mac (both natively and in containers – docker).

Now, Microsoft has a very nice tutorial to get VSCode setup and running on a mac, including some basic configuration (e.g. touchbar support). But when it comes to getting python setup, and running, that is a different matter. Whilst the tutorial is good, it doesn’t actually work and errors out.

Below is the code that Microsoft outlines in the tutorial for python. It essentially is the HelloWorld using packages and is quite simple; but this will fail and won’t work.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 20, 100)  # Create a list of evenly-spaced numbers over the range
plt.plot(x, np.sin(x))       # Plot the sine of each x point
plt.show()                   # Display the plot

When you run this, you will see an error that is something like the one outlined below.

2019-01-18 14:23:34.648 python[38527:919087] -[NSApplication _setup:]: unrecognized selector sent to instance 0x7fbafa49bf10
2019-01-18 14:23:34.654 python[38527:919087] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSApplication _setup:]: unrecognized selector sent to instance 0x7fbafa49bf10'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff521a1ecd __exceptionPreprocess + 256
	1   libobjc.A.dylib                     0x00007fff7e25d720 objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff5221f275 -[NSObject(NSObject) __retain_OA] + 0
	3   CoreFoundation                      0x00007fff52143b40 ___forwarding___ + 1486
	4   CoreFoundation                      0x00007fff521434e8 _CF_forwarding_prep_0 + 120
	5   libtk8.6.dylib                      0x000000011523031d TkpInit + 413
	6   libtk8.6.dylib                      0x000000011518817e Initialize + 2622
	7   _tkinter.cpython-37m-darwin.so      0x0000000114fb2a0f _tkinter_create + 1183
	8   python                              0x0000000101836ba6 _PyMethodDef_RawFastCallKeywords + 230
	9   python                              0x00000001019772b1 call_function + 257
	10  python                              0x0000000101974daf _PyEval_EvalFrameDefault + 45215
	11  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	12  python                              0x0000000101835867 _PyFunction_FastCallDict + 231
	13  python                              0x00000001018b9481 slot_tp_init + 193
	14  python                              0x00000001018c3441 type_call + 241
	15  python                              0x0000000101836573 _PyObject_FastCallKeywords + 179
	16  python                              0x000000010197733f call_function + 399
	17  python                              0x0000000101975052 _PyEval_EvalFrameDefault + 45890
	18  python                              0x0000000101836368 function_code_fastcall + 120
	19  python                              0x0000000101977265 call_function + 181
	20  python                              0x0000000101974daf _PyEval_EvalFrameDefault + 45215
	21  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	22  python                              0x0000000101835867 _PyFunction_FastCallDict + 231
	23  python                              0x0000000101839782 method_call + 130
	24  python                              0x00000001018371e2 PyObject_Call + 130
	25  python                              0x00000001019751c6 _PyEval_EvalFrameDefault + 46262
	26  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	27  python                              0x0000000101836a73 _PyFunction_FastCallKeywords + 195
	28  python                              0x0000000101977265 call_function + 181
	29  python                              0x0000000101974f99 _PyEval_EvalFrameDefault + 45705
	30  python                              0x0000000101836368 function_code_fastcall + 120
	31  python                              0x0000000101977265 call_function + 181
	32  python                              0x0000000101974f99 _PyEval_EvalFrameDefault + 45705
	33  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	34  python                              0x0000000101836a73 _PyFunction_FastCallKeywords + 195
	35  python                              0x0000000101977265 call_function + 181
	36  python                              0x0000000101974f99 _PyEval_EvalFrameDefault + 45705
	37  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	38  python                              0x0000000101836a73 _PyFunction_FastCallKeywords + 195
	39  python                              0x0000000101977265 call_function + 181
	40  python                              0x0000000101974daf _PyEval_EvalFrameDefault + 45215
	41  python                              0x0000000101968a42 _PyEval_EvalCodeWithName + 418
	42  python                              0x00000001019cc9a0 PyRun_FileExFlags + 256
	43  python                              0x00000001019cc104 PyRun_SimpleFileExFlags + 388
	44  python                              0x00000001019f7edc pymain_main + 9148
	45  python                              0x0000000101808ece main + 142
	46  libdyld.dylib                       0x00007fff7f32bed9 start + 1
	47  ???                                 0x0000000000000003 0x0 + 3
)
libc++abi.dylib: terminating with uncaught exception of type NSException

[Done] exited with code=null in 1.017 seconds

The main reason this fails is that one has to be a little more explicit with matplot (the library that we are trying to use). Matplot has this concept of backends, which essentially is the runtime dependencies needed to support various execution environments – including both interactive and non-interactive environments.

For matplot to work on a mac, the raster graphics c++ library that it uses is based on something called Anti-Grain Geometry (AGG). And for the library to render, we need to be explicit on which agg to use (there are multiple raster libraries).

In addition on a mac OS X there is a limitation when rendering in OSX windows (presently lacks blocking show() behavior when matplotlib is in non-interactive mode).

To get around this, we explicitly tell matplot to use the specific agg (“TkAgg in our case) and then it will all work. I have a updated code sample below, which adds more points, and also waits for the console input, so one can see what the output looks like.

import matplotlib
matplotlib.use("TkAgg")
from matplotlib import pyplot as plt
import numpy as np

def waitforuser():
    input("Press enter to continue ...")
    return

x = np.linspace(0, 50, 200)  # Create a list of evenly-spaced numbers over the range
y = np.sin(x)

print(x)
waitforuser()

print(y)
waitforuser()

plt.plot(x,y)
plt.show()

And incase you are wondering what it looks like, below are a few screenshots showing the output.

To get everything working, make sure you setup the Linting, debugger, and the python environment properly. And of course, you can go nuts with containers! Happy coding!

Azure Cognitive Services in containers is the smart way to go

{Cross posted from my post on Avanade}

Containers just got smarter.

That’s the news from Microsoft, which announced recently that Azure Cognitive Services now supports containers. The marriage of AI and containers is a technology story, of course, but it’s a potentially even bigger business story, one that affects where and how you can do business and gain competitive advantage.

First, the technology story
Containers aren’t new, of course. They’re an increasingly popular technology with a big impact on business. That’s because they boost the agility and flexibility with which a business can roll out new tools to employees and new products and services to customers.

With containers, a business can get software releases and changes out faster and more frequently, increasing its competitive advantage. Because containers abstract applications from their underlying operating systems and other services—like virtual machines abstracted from hardware—those applications can run anywhere: in the cloud, on a laptop, in a kiosk or in an intelligent Internet-of-Things (IoT) edge device in the field.

In many respects this frees up the application’s developer, who can focus on creating the best, most useful software for the business. With Microsoft’s announcement, that software can now more easily include object detection, vision recognition, text and language understanding.

At Avanade, we take containers a step further by including support for them in our modern engineering platform, a key part of our overall approach to intelligent IT. So, you can automate your creation and management of containers—including AI-enabled containers—for a faster, easier, more seamless DevOps process. You can take greater advantage of IoT capabilities and move technologies such as AI closer to the edge, where they can reduce latency and boost performance.

What AI containers do for business
And you can do much more, which is where the business story gets interesting. With the greater agility and adaptability that comes with container-based AI services, you can respond more quickly to new competition, regulatory environments and business models. That contrasts with the more limited responses that have been possible with traditional, cloud-based AI. 

For example, data sovereignty laws and GDPR requirements generally restrict the transfer of data to the cloud, where cloud-based cognitive services can interact with it. Now, with containers that support cognitive services, you can avoid those restrictions by running your services locally.

A retail bank might use containerized AI to identify customers, address their needs, process payments and offer additional services, boosting customer satisfaction and bank revenue—all without sending private financial data outside the region (or even outside the bank) in accordance with GDPR.

Similarly, regional medical centers and clinics subject to HIPAA privacy laws in the US can process protected information on site with containerized AI to cut patient wait times and deliver better health outcomes.

Or, think about limited-connectivity or disconnected environments—such as manufacturing shop floors, remote customer sites or oil rigs or tankers—that can’t count on accessing AI that resides in the always-on cloud. Previously, these sites might have had to batch their data to process it during narrow periods of cloud connectivity, with the delays greatly limiting the timeliness and usefulness of AI.

Now, these sites can combine IoT and AI to anticipate and respond to manufacturing disruptions before they occur, increasing safety, productivity and product quality while reducing errors and costs.

If you can’t bring your data to your AI, now you can bring your AI to your data. That’s the message of container-hosted AI and the modern engineering platform. Together, they optimize your ability to bring AI into environments where you can’t count on the cloud. Using AI where you couldn’t before makes innovative solutions possible—and innovative solutions deliver competitive advantage. 

Boost ROI and scale
If you’re already using Azure Cognitive Services, you’ve invested time and money to train the models that support your use cases. Because those models are now portable, you can take advantage of them in regulated, limited-connectivity and disconnected environments, increasing your return on that investment. 

You can also scale your use of AI with a combination of cloud- and container-based architectures. That enables you to apply the most appropriate architectural form for any given environment or use. At the same time, you’re deploying consistent AI technology across the enterprise, increasing reliability while decreasing your operating cost.

Keep in mind…

Here are three things to keep in mind as you think about taking advantage of this important news:

  1. Break the barriers between your data scientists and business creatives. Containerized cognitive services is about far more than putting AI where you couldn’t before. It’s about using it in exciting new ways to advance the business. Unless you have heterogeneous teams bringing diverse perspectives to the table, you may miss some of the most important innovation possibilities for your business.
  2. You need a cloud strategy that’s not just about the cloud. If you don’t yet have a cloud strategy, you’re behind the curve. But if your cloud strategy is limited to the cloud, you may be about to fall behind the next curve. Microsoft’s announcement is further proof that the cloud is crucial to the enterprise—and also part of a larger environment, including both legacy and edge platforms, with which it must integrate.
  3. Be prepared for the ethics issues. Putting cognitive services in places you couldn’t before could raise new ethics issues. After all, we’re talking about the ability to read people’s expressions and even their emotions. This shouldn’t put you off—but it should put you on alert. Plug your ethics committee into these discussions when appropriate. If you don’t already have an ethics committee, create one. But that’s another post. 🙂

Want to learn more?

Microsoft’s announcement furthers the democratization of AI: the use of AI in more places and in more ways throughout the enterprise and beyond. Whether you turn to us for your AI solutions or look to us to assist you in developing your own, we’re ready to help with the greatest concentration of Microsoft expertise outside of Microsoft itself.

Bugs

It is a known bug with the programming language. 🙂

#GeekyJokes #ProgrammerHumor

Docker container running Ubuntu on Windows

Containers are all the rage right now and rightfully so – not only do they help abstract away some of the complexity and dependencies of your apps and solutions, they also make managing of environments, and, deployments much simpler. And the fact that you can do it in a consistent, and repeatable fashion is just icing on the cake.

As a simple example, with Docker, on Windows (as in my case), I can run a dockerized app, on a different OS than the host, which can also be interactive. 

The command below will spawn a container, pull down the image of Ubuntu and then run an interactive terminal, tying the terminal to the standard input. Of course in this example, this requires that you already have Docker installed (the Community Edition would be just fine to play around with).

docker run --interactive --tty ubuntu bash

Now, with Docker if you do get the following error (on Windows): “Error response from daemon: operating system on which parent image was created is not Windows.” as also shown below, the way to fix it is to switch on Experimental features.

Docker error when trying to run Ubuntu on Windows

To try and fix this, right click on the docker icon in the system tray, choose Settings, and from the setting screen, in the Daemon tab, enable experimental features as shown below.

And after enabling the experimental features, the docker daemon will restart. And post that, if you run the docker command again, it would work as expected:

  • It pulls down the image (which is used to run in the container)
  • Runs Ubuntu in an interactive session (this is because of the option I choose)
  • And all within my PowerShell console on Windows.

This is just the beginning, there of course is a lot more to it.  🙂