   [actions]
   (->> actions
        (map :joints)
-       (filter identity)))
-
-(defn do-joints
-  "Make action for joint changes if any"
-  [joint-lists duration]
-  (if-let [{:keys [names values]}
-           (cond
-            (> 1 (count joints)) (reduce concat_joints joints)
-            (= 1 (count joints)) joints)]
-    ))
+       (filter identity)
+       (apply merge)))
+
+(defn- clip
+  [value min max]
+  (cond
+   (> value max) max
+   (< value min) min
+   :else value))
+
+(defn- compute-joint-speed
+  "Compute fraction of max speed for joint movement in radians"
+  [execution-time-seconds cur-pos-rad desired-pos-rad limits]
+  (let [max-change (limits 2)
+        desired-change (Math/abs (- cur-pos-rad desired-pos-rad))
+        speed (/ desired-change (* max-change execution-time-seconds))]
+    (clip speed 0.01 1.0)))
+
+(defn- motion-task
+  [robot joint-name joint-pos-deg execution-time-seconds cur-joints limits]
+  (let [pos-rad (Math/toRadians joint-pos-deg)
+        speed-fraction (compute-joint-speed execution-time-seconds
+                                            (cur-joints joint-name)
+                                            pos-rad
+                                            (limits joint-name))]
+    (call-service robot :motion "angleInterpolationWithSpeed"
+                  [joint-name
+                   (into-array Object [(Float. pos-rad)])
+                   (Float. speed-fraction)])))
+
+(defn- do-joints
+  "Takes a map of joint changes and a duration and builds a motion task for each joint with the appropriate speed"
+  [robot duration joint-changes]
+  (if (seq joint-changes)
+    (let [cur-joints (get-joint-angles-internal robot Boolean/FALSE)
+          limits (load-joint-limits robot)]
+      (map (fn [[j v]] (motion-task robot j v duration cur-joints limits))
+           joint-changes))))
 
 ;; TODO make the duration and channel params optional
 (defn donao
   "Accepts a number of parameters specifying robot actions and executes them"
   [robot duration chan & actions]
-  (let [joint-changes (only-joint-actions actions)]
+  (let [joint-changes (only-joint-actions actions)
+        joint-tasks (do-joints robot duration joint-changes)]
     ))

(defn get-installed-behaviours
  "Return list of behaviours installed on robot"
  [robot]
  (call-service robot :behaviour-manager "getInstalledBehaviors" ))

(defn get-running-behaviours
  "Get list of running behaviours"
  [robot]
  (call-service robot :behaviour-manager "getRunningBehaviors"))

(defn get-default-behaviours
  "Get list of default behaviours (behaviours set to run automatically)"
  [robot]
  (call-service robot :behaviour-manager "getDefaultBehaviors" ))

(defn add-default-behaviour
  "Make the named behaviour auto run on robot boot"

  (->
   (call-service robot :motion "getBodyNames"  [name])
   (.get)))

(defn get-joint-limits
  "Get the minAngle (rad), maxAngle (rad), maxVelocity (rad.s-1) and maxTorque (N.m). for a given joint or actuator in the body."
  [robot]
  (->>
   (call-service robot :motion "getLimits" ["Body"])
   (.get)
   (zipmap joint-names)))

(defn save-joint-limits
  "Get joint limit information and save it in robot structure"
  [robot]
  (assoc robot :joint-limits (get-joint-limits robot)))

(defn load-joint-limits
  "Get joint limits from robot map or retrieve them from NAOqi"
  [robot]
  (if-let [limits (:joint-limits robot)]
    limits
    (get-joint-limits robot)))

;; broken - can't construct variant holding array of values
(defn set-joint-angles
