Wireless Roaming: Ubiquiti UniFi AP AC Lite vs. UniFi AP vs. Apple Airport Extreme / Express

In meinem Haus gibt es überall Ethernet, aber nicht überall WLAN. Das ist ärgerlich, kein Router schafft es, das ganze Haus alleine auszuleuchten. Getestet bisher: FritzBox 7270, Telekom Speedport Hybrid, Apple Airport Extreme, Ubiquiti UniFi AP LR, TP-Link 741.

Ich möchte aber im ganzen Haus ordentliche Bits haben und handle mal kurz ab, wie ich versuche zu einer Entscheidung zu kommen.

Die naheliegende Lösung

fritzboxTL-WR841ND

Man rennt los und holt sich einen zweiten Access Point. Diese wird mit gleichen Einstellungen (ESSID, Kanal, Passwort) als Bridge zwischen LAN und WLAN betreiben. Das Problem an der Sache: die mobilen Clients „kleben“ an ihrem Access Point. Wenn man sich durch das Haus bewegt, wird die WLAN-Verbindung immer schlechter. Der Client hängt dann vielleicht mit 1MBit am Access Point in der oberen Etage und schafft kaum ein Paket zu empfangen, während man mit ihm neben dem Access Point in der unteren Etage steht. Schon fast reflexartig knipst man dann das WLAN am Mobiltelefon aus und wieder an, damit der nähere Access Point gewählt wird. Das ist nervig.

Die politisch inkorrekte Lösung

WLAN-Repeater kommen für mich prinzipbedingt nicht in Frage:

  • Die Daten sind doppelt in der Luft, die Bandbreite leidet (gute Repeater können auf zwei Bändern funken und werden beim repeaten wenigstens nicht langsamer).
  • Repeater machen „Magie“. Sie müssen tricksen und machen so eine Art NAT auf Layer 2, jeder Hersteller kocht da seine eigene Suppe. Disclaimer: dieser Punkt ist gefährliches Halbwissen, was ich nebenbei aufgeschnappt habe, in meinem Kopf blieb lediglich haften: Repeater sind grundsätzlich scheiße. Sie mögen funktionieren, sind aber immer noch der schlechteste Kompromiss.
  • Es wiederspricht meinem gesunden Menschenverstand: ich habe überall Gigabit LAN, Daten gehören ins Kabel, nicht in die Luft

Die teure Lösung

airport_extreme
airport_express

Weil ich beruflich bedingt, aber auch sonst Apple-Fanboy bin, habe ich mir eine Airport Extreme geklickt. Dazu gibt es noch eine Airport Express für die untere Etage. Die Hoffnung ist, dass hier Roaming funktioniert. Die Airports sind sehr teuer, haben wenig Optionen und wollen alles für einen regeln. Leider musste ich feststellen, dass es (in meinem Netzwerk) nicht möglich ist die beiden Geräte kabelgebunden zu betreiben. Sobald beide Geräte am LAN hängen, geht gar nichts mehr: die WLAN-Verbindung an meinem Notebook bricht ständig zusammen, Paketverluste ohne Ende und natürlich kein Log, keine Info, keine Fehlermeldung – Apple halt.

Ich habe die beiden Airports dann so betrieben, dass die Express benutzt wurde um das WLAN der Extreme zu vergrößern (das scheint eine Art WDS oder Repeater-Funktion zu sein). Diese „Range-extender“-Lösung funktioniert soweit ganz gut. Ich kann zumindest durchs Haus laufen und i.d.R. hängt mein Telefon am „richtigen“ Access Point und hat eine gute WLAN-Verbindung.

Es ist jedoch irgendwie unbefriedigend, weil die Daten der Airport Extreme immer zweimal durch die Luft gehen, obwohl in unmittelbarer Nähe eine LAN-Dose zur Verfügung steht.

Die „richtige“ Lösung

ubiquiti_uap_ac_liteubiquiti_uap_ac_lite

Natürlich gibt es auch eine „richtige“ Lösung: Managed WLAN mit einem WLAN-Controller. D.h. eine Instanz im LAN spricht mit allen Access Points und kann entscheiden, welcher Client an welchem Access Point hängen sollte. Das Roaming zwischen den Access Points ist die hohe Kunst.

Es gibt dafür sehr teure Lösungen und es gibt etwas von Ubiquiti. Also habe ich mir zwei UniFi AC Lite Ufos geklickt. Das Paket war noch nicht ausgepackt, da chattet mich Kollege S. an. Er hat das gleiche Problem (große Wohnung, viel LAN, wenig WLAN) und hat festgestellt, dass Zero-Handoff scheinbar nicht mit AC-Geräten von Ubiquiti funktioniert. Wenn man danach googelt ist man nicht schlauer, in den Foren des Herstellers gibt es seitenlange Threads aus denen hervor geht, dass Zero-Handoff mit AC-Geräten möglicherweise nie funktionieren wird. Gründe dafür findet man aber nicht.

Was tun? Genau: nachmessen.

Der große Showdown

Ich habe zu testzwecken noch zwei Ubiquiti UniFi APs geklickt. Die können „nur“ 802.11n auf 2.4GHz. Das ist für meine Anwendung aber ausreichend (Twitter muss auf dem Klo funktionieren). Große Datenmengen schiebe ich am LAN-Kabel durch die Gegend und in der Praxis ist dann 802.11ac auch nicht sooo viel schneller.

Mein Benchmark ist: welche Geräte schaffen sauberes WLAN-Roaming? Ich möchte also durchs Haus laufen und möglichst immer Netz haben.

Die gewählte Messmethode ist hochwissenschaftlich: ping. Ich pinge 10x pro Sekunde mein Telefon an und laufe damit immer wieder die gleiche Strecke durchs Haus. Wenn ich unten in der Küche angekommen bin, wechselt das Telefon zum unteren AP. Auf dem Weg nach oben erfolgt ebenfalls nochmal ein Wechsel des Access Points. Damit das ganze etwas aussagekräftiger wird, wiederhole ich die Messung drei Mal. Dann teste ich noch verschiedene Paketgrößen: 56, 504 und 1492 Bytes.

Nachdem ich also 36 Mal durch das Haus gewandert bin und unter den irritierten Blicken der Familie Zahlen murmelnd wieder verschwunden bin kann ich folgendes verkünden:

Size UniFi AP UniFi AP ZH UniFi AC Airport
56 0,48% 5,66% 5,40% 5,63%
504 2,40% 5,61% 2,63% 12,51%
1492 5,01% 11,28% 17,42% 28,18%

wlan_showdown

In Worten bedeutet es, dass die Airports am meisten Pakete verlieren, wenn ich durchs Haus gehe. Die UniFi APs verlieren am wenigsten und die UniFi AC sind irgendwo dazwischen. Interessanterweise performt der UniFi AP mit eingeschaltetem Zero-Handoff schlechter. Die besten Ergebnisse bringt der UniFi AP ohne Zero Handoff.

Damit ist Entscheidung nicht einfacher: Zero Handoff funktioniert scheinbar nicht wirklich, außerdem muss man sich auf einen Kanal festlegen und hat nur noch die halbe 802.11n Bandbreite (HT20). Nachdem ich die ganze Messung durchgeführt habe, grübel ich schon wieder, was wichtiger ist: nahtloses Roaming (was es so nicht gibt) oder gute Bandbreite an einem bestimmten Platz im Haus (die ich eigentlich nicht benötige).

Disclaimer

  • Das Ergebnis ist nur für meine Situation anwendbar. Jedes Haus, jede WLAN-Umgebung ist anders.
  • Es ist natürlich etwas unfair, weil die Airport Express nicht am LAN hängt. Die Datenmenge ist viel zu klein und das ganze Vorgehen ist höchst unwissenschaftlich. Ich bin halt Praktiker 😉
  • Jolly hat vor einem Jahr die Performance der UniFi APs viel genauer nachgemessen und festgestellt, dass es bei den Geräten Dropouts gibt. Das war nicht Teil meiner Untersuchung. Möglicherweise gibt es inzwischen einen Fix in der UniFi Firmware für das Problem. Ich konnte bisher keine Probleme feststellen. YMMV.

An one finger rotation gesture recognizer

Last year we developed the Raumfeld iPhone App. The goal was to build something that could replace our native controller completely. The main design feature of the native controller is the massive volume knob. Of course we wanted to have something similar in our App, so the designer created the volume screen with a big knob, as shown in the right picture.

To create a volume control, we needed a way to detect if the user performs a rotation gesture on the knob image. There is no default UIGestureRecognizer that detects such a gesture, so we had to implement a custom gesture recognizer. It was surprisingly easy – to be honest, the hardest part was to do the math right.

To track the finger movement, we need to check for every touch event, whether it is within the defined area. The gesture is rotating, so there is a center point m, the radius a which defines the minimum distance from m and the radius b which defines the maximum distance from m. Finally we need to calculate the angle ? between the startpoint and the current finger position (relative to m).

For the first check, we calculate the distance d from the center and make sure, that a < d < b is true.

/** Calculates the distance between point1 and point 2. */
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2)
{
    CGFloat dx = point1.x - point2.x;
    CGFloat dy = point1.y - point2.y;
    return sqrt(dx*dx + dy*dy);
}

The rotation angle is the angle between the two lines a and b. The arc tangent function atan() is the key:

/** The method is a bit too generic - in our case both lines share the same start point. */
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
                                   CGPoint endLineA,
                                   CGPoint beginLineB,
                                   CGPoint endLineB)
{
    CGFloat a = endLineA.x - beginLineA.x;
    CGFloat b = endLineA.y - beginLineA.y;
    CGFloat c = endLineB.x - beginLineB.x;
    CGFloat d = endLineB.y - beginLineB.y;
 
    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);
 
    // convert radiants to degrees
    return (atanA - atanB) * 180 / M_PI;
}

Ok, that was the hard part 🙂 To implement a custom gesture recognizer, let’s have a look at the UIGestureRecognizer API docs, especially the subclassing notes. We just need to overwrite five methods

- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

The key is touchMoved:withEvent: This method checks the distance of the touch event from the center point. If the touch is within the valid area, the angle between the start point and the current touch position is calculated. The result is sent to the delegate object of our class.

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
 
    if (self.state == UIGestureRecognizerStateFailed) return;
 
    CGPoint nowPoint  = [[touches anyObject] locationInView: self.view];
    CGPoint prevPoint = [[touches anyObject] previousLocationInView: self.view];
 
    // make sure the new point is within the area
    CGFloat distance = distanceBetweenPoints(midPoint, nowPoint);
    if (   innerRadius < = distance
        && distance    <= outerRadius)
    {
        // calculate rotation angle between two points
        CGFloat angle = angleBetweenLinesInDegrees(midPoint, prevPoint, midPoint, nowPoint);
 
        // fix value, if the 12 o'clock position is between prevPoint and nowPoint
        if (angle > 180)
        {
            angle -= 360;
        }
        else if (angle < -180)
        {
            angle += 360;
        }
 
        // sum up single steps
        cumulatedAngle += angle;
 
        // call delegate
        if ([target respondsToSelector: @selector(rotation:)])
        {
            [target rotation:angle];
        }
    }
    else
    {
        // finger moved outside the area
        self.state = UIGestureRecognizerStateFailed;
    }
}

target needs to implement some kind of protocol to allow the gesture recognizer notification of movements:

@protocol OneFingerRotationGestureRecognizerDelegate <nsobject>
@optional
/** A rotation gesture is in progress, the frist argument is the rotation-angle in degrees. */
- (void) rotation: (CGFloat) angle;
/** The gesture is finished, the first argument is the total rotation-angle. */
- (void) finalAngle: (CGFloat) angle;
@end
</nsobject>

And that’s the whole magic. To use the gesture recognizer, create an image of a rotating control and add a gesture recognizer to your view:

// calculate center and radius of the control
CGPoint midPoint = CGPointMake(image.frame.origin.x + image.frame.size.width / 2,
                               image.frame.origin.y + image.frame.size.height / 2);
CGFloat outRadius = image.frame.size.width / 2;
 
// outRadius / 3 is arbitrary, just choose something >> 0 to avoid strange 
// effects when touching the control near of it's center
gestureRecognizer = [[OneFingerRotationGestureRecognizer alloc] initWithMidPoint: midPoint
                                                            innerRadius: outRadius / 3 
                                                            outerRadius: outRadius
                                                                 target: self];
[someView addGestureRecognizer: gestureRecognizer];

As soon as a gesture is detected, the delegate method is called and you can rotate the image according to the angle:

- (void) rotation: (CGFloat) angle
{
    // calculate rotation angle
    imageAngle += angle;
    if (imageAngle > 360)
        imageAngle -= 360;
    else if (imageAngle < -360)
        imageAngle += 360;
 
    // rotate image and update text field
    image.transform = CGAffineTransformMakeRotation(imageAngle *  M_PI / 180);
    textDisplay.text = [NSString stringWithFormat: @"\u03b1 = %.2f", imageAngle];
}

I’ve created a sample project on github, feel free to play with. Also make sure to read Ole Begemanns article about the UX details on gesture recognition.

Objective C: KVC und KVO

Ich empfinde das Eintauchen in die Welt von Objective C und Cocoa als sehr erfrischend, jeden Tag gibt es etwas neues zu entdecken. Wenn man sich mit anderen Entwicklern, die Objective C nicht kennen, unterhält, kommt aus der C++ Ecke garantiert die Frage: „Kann man dort Operatoren überladen?„.

Nein kann man nicht. Braucht man auch gar nicht. Operatoren zu überladen ist zweifelsohne cool, aber ich multipliziere nicht den ganzen Tag Matrizen, sondern baue Dinge für Endanwender. Das angenehme an Objective C ist, dass es bestimmte Real World Probleme sehr schön löst.

Key Value Observing ist so eine Lösung und es ist ein so verdammt einfaches Konzept, dass ich mich wundere, wie die Welt bisher ohne leben konnte… (Um es vorweg zu nehmen: GObject hat ein ähnliches Konzept und es gibt bestimmt noch mehr Implementierungen dieses Patterns).

Das Real World Problem kommt bestimmt jedem bekannt vor: man hat eine Klasse A gebaut und eine andere Klasse B muss benachrichtigt werden, wenn sich in A irgendetwas interessantes ändert. Bisher wäre das Herangehen, dass A eine Referenz auf B mitschleifen muss, damit Klasse A die Klasse B benachrichtigen kann. Das bringt natürlich das schöne Klassendesign durcheinander, eigentlich hat A mit B nichts zu tun und die Referenz auf B zu handhaben ist auch nervig.

In Objective C gibt es Properties, die Membervariablen und deren Getter und Setter erzeugen können:

// foo.h
@interface Foo
@property(nonatomic, copy) NSString* bar;
@end
 
// foo.m
@implementation Foo 
@synthesize bar;
@end

Im obrigen Beispiel hat die Klasse „Foo“ ein Member „bar“ und @synthesize generiert die Getter und Setter, die dann so verwendet werden können:

Foo *fooInstance = [Foo new]; // oder [[Foo alloc] init]
fooInstance.bar = @"Value";
NSLog(@"foo.bar=%@", fooInstance.bar); // "foo.bar=Value"
 
[fooInstance setBar:@"eulaV"]; // geht auch
NSLog("@foo.bar=%@", [fooInstance bar]); // "foo.bar=eulaV"
 
[fooInstance release];

Soweit, so trivial. Mit Key Value Coding kann man auf Properties programmatisch zugreifen. Das bedeutet, ich baue mir einen String zusammen, dessen Inhalt der Name einer Property ist und ich kann auf die Property zugreifen:

NSString* dings = [fooInstance valueForKey:@"bar"]; // "getter"
[fooInstance setValue:@"Hi there!" forKey:@"bar"]; // "setter"

Das wird unheimlich praktisch, wenn man z.B. XML parsen muss und die geparsten Key/Value-Paare direkt via KVC in den Properties einer Klasse speichern kann.

Key Value Observing ist nun die Lösung o.g. Problems: man kann sich einfach von außen als Observer für bestimmte Properties einer Klasse registrieren und wird benachrichtigt, falls sich ein Wert ändert.

Das spart eine Menge Code und die beobachtete Klasse A braucht sich um Klasse B nicht kümmern und muss keine Referenz auf B mitschleifen.

// als Observer registrieren ist einfach:
[fooInstance addObserver:self   // sag mir bescheid,
              forKeyPath:@"bar" // wenn sich bar ändert
               options:NSKeyValueObservingOptionNew
               context:NULL];
 
// der Observer muss diese methode implementieren, 
// um benachrichtigt zu werden:
- (void) observeValueForKeyPath:(NSString *)keyPath
		     ofObject:(id)object
		       change:(NSDictionary *)change
		      context:(void *)context 
{
    if ([keyPath isEqualToString:@"bar"])
        NSLog(@"new value for bar: %@", object);
}
 
// wenn man genug hat, meldet man sich wieder ab
[fooinstance removeObserver:self forKeyPath:@"bar"];

Ich finde das ziemlich elegant. Das Klassendesign bleibt sauber und trotzdem ist es möglich, sich kreuz und quer durch alle Schichten der Anwendung über Zustandsänderungen benachrichtigen zu lassen.

Update: Es gibt einen sehr schönen Wrapper von Andy Matuschak: Block Callbacks for Cocoa Observers (via Ole Bergmanns hoch interessantem Twitterstream)

Schnelle Übersetzung im Safari

Ich lese viel englische Texte und bin dank „Finer Things in Mac“ auf ein Feature gestoßen, das mir beim Lesen sehr weiterhilft: der Shortcut CTRL + CMD ⌘ + D.

Dictionary.appDamit kann man in jeder Cocoa-Applikation (z.B. Safari) das Wort unter dem Mauscursor im Mac OS X eingebauten Wörterbuch nachschlagen. Als Standard wird das New Oxford American Dictionary mitgeliefert, das nur von Englisch nach Englisch „übersetzt“. Zum Glück gibt es Abhilfe: das BeoLingus Dictionary-Plugin Deutsch/Englisch ermöglicht die Übersetzung von Englisch nach Deutsch und umgekehrt.

Nach der Installation des Plugins und dem Neustart der Dictionary-Anwendung muss jetzt nur das Deutsch/Englisch Wörterbuch als erstes Wörterbuch festgelegt werdem. Dieses wird bei CTRL + CMD ⌘ + D verwendet, die anderen Wörterbucher sind Fallback, wenn das Wort nicht gefunden wird. Die bevorzugte Reihenfolge der Wörterbücher kann man in den Einstellungen von Dictionary.ap festlegen. Einfach das „German-English“ Wörterbuch vom Ende der Liste an den Anfang der Liste ziehen…

Dictionary Settings

…und schon muss man nie wieder die Anwendung wechseln, nur um ein Wort nachzuschlagen:

Dictionary

HFS+ unter Linux beschreiben

Ich habe heute zu meinem Erstaunen festgestellt, dass man HFS+Volumes – dem Fall meine externe USB-Platte – nicht unter Linux beschreiben kann. Nachdem ich google eine Weile mit Suchbegriffen („Read only filesystem“ „hfsplus“) beworfen hatte, kam auch ans Licht, woran das liegt.

Linux kann schon HFS+-Volumes beschreiben, allerdings nicht, wenn diese journaled sind. Die Lösung, das Journal abzuschalten, ist für mich akzeptabel. Unter Mac OS X geht das am schnellsten auf der Kommandozeile:

1
$ sudo /usr/sbin/diskutil disableJournal /Volumes/BACKUP

Mein kleiner Bauchladen

iMac 20inch

Kurz und schmerzlos: iMac white, 20 Zoll Display, keine Pixelfehler, 2 GHz core duo, 2 GB Ram, 250 GB Festplatte, mehr technische Details bei everymac. Tastatur der Marke „Krümelschublade“ und die tolle magnetische Apple Remote sind natürlich dabei.

War mein treues Arbeitspferd in den letzten 2.5 Jahren und ist nach wie vor eine geniale Surf- Multimedia- und Photoshop-Maschine. Ich trenne mich nur unter großen Schmerzen davon, ein realistisches Gebot per Mail kann meine Trauer jedoch mildern. Ansonsten kommt der Rechner Montag zu ebay.

Reinstall

Nachdem mein Mac permanent an der Kotzgrenze war, habe ich heute kurzerhand Mac OS neu installiert. Man sollte ja nicht denken, dass soetwas notwendig ist und Erfolg bringt. Aber die Kiste ist jetzt genau 3 Jahre alt, hat diverse Software übergebügelt bekommen, ein Upgrade von 10.4 auf 10.5 hinter sich und war bei der täglichen Arbeit dauernd am swappen.

Also DVD rein und alles neu macht der Mai. Interessant finde ich, wie schnell man wieder arbeitsfähig ist. Einerseits, weil natürlich der meiste Kram sowieso im Netz liegt, aber auch, weil ich schätzungsweise nur 20% der installierten Applikationen genutzt habe. Der Rest fällt in die Kategorie „einmal ausprobiert, nie wieder angefasst“.

Ich habe deswegen auch nicht das Timemachine-Backup zurückgespielt, sondern mir selektiv die Daten wieder geholt, die ich wirklich brauche. Ansonsten bestände die Gefahr, sich den unbekannten Speicherfresser gleich wieder an Board zu holen.

istatsmenuCool auch, dass man sich die vormals installierten Anwendungen einfach wieder vom Backup mit dem Finder zurückkopieren kann. Würde ich alle Programme nochmal downloaden müssen, wäre ich jetzt noch lange nicht fertig. Aber so: drag, drop fertig.

Das Resultat ist ziemlich überwältigend, nach 12 Stunden uptime und diversen Klickorgien ist nach wie vor 1 von 2 GB Speicher verfügbar. Das war vorher vielleicht der Zustand kurz nach dem Booten.

Einziger Nachteil: das Argument, warum ich unbedingt einen Mac Pro brauche, ist natürlich erstmal wieder vom Tisch 😉

Watermark export action for Adobe Lightroom

20090127_watermark_1I watermark my photos in my photoblog using a little script (yes, I know this…). My workflow so far was

  1. export the photos from Lightroom
  2. open finder, go to the export directory
  3. open a shell in the directory
  4. run my watermark-script for the exported pictures

Well, this is annoying. But there is a gentle solution: Platypus! This great piece of software turns every Shell/Python/Perl/Whatever script into a little Mac-Application which can be used as export action in Lightroom.

To create your own export action using Platypus, just open the application and point it to the script path using the „Select“ button. Platypus is smart and will fill in more essentials:

20090127_platypus_1

Now press the „Create“ button and select the Lightroom Export Actions directory. It is usually
/Users/yourname/Library/Application Support/Adobe/Lightroom/Export Actions
Open Lightroom, select the pictures you want to export and press the Export-button (or Command Shift E). At the bottom of the export dialog you find the postprocessing options. The „after export“ combobox should now display the newly created export action:

20090127_export_action

Now press „export“ and watch what happens:
20090127_exporting

That’s it. Great isn’t it? 🙂 Here is my watermark-script. It needs exiv2 and imagemagick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/usr/bin/python
 
import sys
import re
import os
import subprocess
import string
import shutil
 
# path exiv2 executable
EXIVBINARY="/usr/local/bin/exiv2"
# path composite executable (from imagemagick)
COMPOSITEBINARY="/opt/local/bin/composite"
# watermark image
WATERMARKFILE="/Users/melle/Pictures/watermark/Watermark2.png"
 
for filename in sys.argv[1:]:
 
    if not os.path.exists(filename):
        print filename + " does not exist!"
        continue
 
    # extract some strings from the filename
    pathRe = re.compile( '^.*\/')
    plainFilename   = pathRe.sub( '', filename)
    baseFilename    = string.replace(plainFilename, ".jpg", "")
    newFilename     = "mark_" + plainFilename
    newMetaFilename = "mark_" + re.compile('\.[jJ][pP][gG]').sub('.exv', plainFilename)
 
    print "Tagging " + plainFilename
 
    # get the path - if available
    m = pathRe.search(filename)
    if m:
        pathname = m.group()
    else:
        pathname = ""        
 
    # backup metadata to exv-file
    p = subprocess.Popen(EXIVBINARY + " -f ex \"" + filename + "\"", shell=True)
    sts = os.waitpid(p.pid, 0)
 
    # mv "$BASENAME.exv" "$NEWMETAFILENAME"
    shutil.move(pathname + baseFilename + ".exv", pathname + newMetaFilename)
 
    # add watermark
    p = subprocess.Popen("/opt/local/bin/composite -unsharp 2.0x1.4+0.6+0.05 -dissolve 30 -gravity southeast -geometry +25+25 \"" + WATERMARKFILE + "\" \"" + filename + "\"  \"" + pathname + newFilename + "\"" , shell=True)
    sts = os.waitpid(p.pid, 0)
 
    # restore metadata
    p = subprocess.Popen(EXIVBINARY + " -f in \"" + pathname + newFilename + "\"", shell=True)
    sts = os.waitpid(p.pid, 0)
 
    # delete metadatafile
    os.remove(pathname + newMetaFilename)
 
    # open file in finder (mac only)
    p = subprocess.Popen("/usr/bin/open -a Finder \"" + pathname + "\"", shell=True)

WebKit

Die nightly builds von WebKit (aka Safari) haben Squirrelfish, eine neue JavaScript-Engine, an Board. Das Teil ist verdammt schnell. Benchmark-Balken sind das eine, aber wenn typische Browser-Killer plötzlich butterweich laufen, fällt man echt ins Essen.

Mein Favorit für soetwas ist die OLSR-Viz in der Freifunk-Firmware. Wenn das eine halbe Stunde im Firefox läuft, kann man den Prozess eigentlich nur noch killen.
Wer es selbst ausprobieren möchte, kann ja das Viz von meinen Router nehmen. Die Metrik kann ruhig auf 8 erhöht werden und den Vergleich zwischen den verschiedenen Browsern sollte man auch mal machen.

Andere Javascript-Lastige Seiten, z.B. alles aus dem Hause google, sind jetzt deutlich flüssiger zu bedienen. Ich bin echt begeistert. Noch schnell Adblock für Safari installieren und schon kann der lahme Firefox in Rente geschickt werden 🙂 [via]

Nokia E65 und isync

Wenn das offizielle isync-Plugin von Nokia sich beschwert, dass man zuerst isync aktualisieren soll, obwohl man die geforderte isync-Version 2.4 installiert hat, dann sollte man sicherstellen, dass isync wirklich im Ordner „Programme“ installiert ist (und nicht in einen Ordner unterhalb verschoben wurde).

ironisch

Ironically, to make Firefox your default browser under Mac OS X, you have to run Safari, select Preferences, and set Default Web Browser to Firefox.“ [#]

Egal, hauptsache der richtige Browser tut es.

Backup

Die Geizmarkt-Geschenkgutscheine der letzten Jahre in eine allerwelts-„Backuplösung“ investiert. Jetzt langsam das Gewissen beruhigen, weil die so kostbaren Werke von nun an die Chance auf ein Überleben haben. Nicht, dass ich vorhier nie meine Daten gesichert hätte, aber DVD-RAM ist bei den Mengen einfach ein Witz, was Medienpreis und Schreibgeschwindigkeit angeht.

Auch nett: „Wie formatiere ich eine externe Festplatte?„. Ausgeliefert wird das Teil NTFS-formatiert. Ungünstig, wenn man nur Mac OS X und Linux benutzt und eine gelegentlichte Anstöpselung bei befreundeten Windows-Maschinen angedacht ist.

NTFS schreiben kann man unter Mac OS X und Linux vergessen. HFS+ wäre ein heißer Kandiat, wäre da nicht die „ab und zu mal“ Windows-Connectivität. Also schweren Herzens FAT32 gewählt. Natürlich kann eine Partition mit dem XP-Festplattenmanager maximal 32 GB groß sein. Partition Magic schafft immerhin 190GB – keine Ahnung, welche Grenze dort wieder zuschlägt. Zumindest habe ich jetzt zwei halbwegs gleich große Partitionen, die ich von überall beschreiben kann.

Jetzt heisst es abwarten.

Backup

Die gleiche Menge Musik wartet noch auf das Rettungsboot. Ich denke derweil über eine gute Automatisierungslösung nach, in der rsync eine Rolle spielt.

Apfelpost

Ich bin ein Mail-Junkie. „Mein Gott, bist Du kommunikativ!“ ist noch ein Vorwurf der netten Sorte.
Was ich nicht in meinem Kopf habe, liegt in meiner INBOX. Die Suchfunktion des Mailprogramms hat schon so manche Gedächtnislücke gefüllt. Seit etlichen Jahren schwöre ich auf die Kombination aus IMAP und Eichhörnchen, damit ich auch von überall auf der Welt an meine Mails komme und ich nicht zwei Postfächer pflegen muss.
Seit dem Apfel auf meinem Schreibtisch versuche ich mit Mail.app glücklich zu werden. Bisher klappt es auch ganz gut, es gibt aber ein paar geniale Tools, die das ganze noch komfortabler machen.

Mail Appetizer

Mail Appetizer zeigt neue Mails in einem halbtransparenten Vorschaufenster an. Man kann also mit einem kurzen Blick prüfen, ob es sich lohnt das Mailprogramm nach vorne zu holen. Das kling simpel, ist aber extrem nützlich. Und so sieht’s aus:

Mail appetizer

IMAP-IDLE

IMAP-IDLE stattet Mail.app mit Unterstützung für das IDLE-Feature von IMAP aus. Wenn der Mailserver dieses Feature unterstützt, benachrichtigt er Clients sofort beim Eintreffen von neuen Mails. Mail.app muss also nicht mehr den Server im Minutentakt nach neuen Mails fragen.

Für Courier-IMAP muss man in der /etc/courier/imapd das Idle-Feature mit folgender Zeile aktivieren:

IMAP_ENHANCEDIDLE=1

IMAPCheck

Leider Prüft Mail.app nur die INBOX selbst und keine Unterordner. Wenn Mails direkt vom Mailserver in die Ordner sortiert werden, merkt man so lange nichts davon, bis man nicht auf diesen Ordner klickt.
Dieses Problem behebbt IMAPCheck: damit werden alle IMAP-Ordner nach neuen Nachrichten geprüft.

MacOS X und die Netzlaufwerke

Ich haben endlich das leidige Problem mit den Netzlaufwerken und dem Ruhezustand gelöst. Mit Sleepwatcher kann man so ziemlich jede Aktion beim Einschlafen bzw. Aufwachen seines Macs ausführen lassen.
Egomanen lassen sich bei jedem Aufwachen von der eingebauten iSight-Kamera fotografieren und bekommen auf diesem Wege interessante Einblicke in die Service-Abteilung von Apple 🙂 Meiner einer ist nicht ganz so selbstverliebt und begnügt sich mit dem Ausführen einiger AutomountMaker-Scripte. Das .wakeup Script sieht jetzt bei mir so aus:

#!/bin/sh
/Applications/Utilities/AutomountMaker/AutomountMaker.app/Contents/MacOS/AutomountMaker \
  /Users/melle/Documents/automount/mount-mp3.amsf \
  /Users/melle/Documents/automount/mount-shared.amsf \
  /Users/melle/Documents/automount/mount-melle.amsf

Wasserzeichen automatisiert

Für das Markieren von Bilder mit Wasserzeichen gibt es diverse Software, ich habe aber kein Programm gefunden, das meinen Ansprüchen genügt. Selbst ist der Nerd. Für ImageMagick genügt der folgende Einzeler:

composite -dissolve 30 -gravity southeast -geometry +25+25 "/path/to/watermark.png" $FILE $NEWFILENAME

Leider gehen dabei manche Metadaten wie z.B. Keywords verloren. Mit dem exiv2-Tool ist das jedoch kein Problem, die Daten werden aus dem Originalbild exportiert und in die markierte Datei importiert. In ein Shellscript gegossen sieht das ganze dann so aus:

#!/bin/sh
#
for FILE in $*
do
  echo processing $FILE
  BASENAME=`echo $FILE | sed 's/\(.*\)\.[jJ][pP][gG]/\\1/g'`
  NEWFILENAME=marked_$FILE
  NEWMETAFILENAME=marked_$BASENAME.exv
  # backup metadata from file
  /usr/local/bin/exiv2 -v ex $FILE
  mv $BASENAME.exv $NEWMETAFILENAME
  # apply watermark
  composite -dissolve 30 -gravity southeast -geometry +25+25 \\
     "/path/to/watermark.png"  $FILE $NEWFILENAME
  # restore metadata
  /usr/local/bin/exiv2 -v in $NEWFILENAME
  rm $NEWMETAFILENAME
done

Die exiv2-Version in Darwinports ist leider hoffnungslos veraltet und scheitert an den von ImageMagick erzeugten Bildern, mit der aktuelle Version 0.11 ist das jedoch kein Problem. Die Installation erfolgt wie schon zu Großvaters Zeiten mit $ ./configure && make && sudo make install.

Apfelmuß

Ich habe ein paar Fragen an die Apfel-Auskenner:

  • Gibt es mit der gewaltigen Maus keinen Rechtsklick? Ich muss immer CTRL-klicken um an das Kontextmenü ranzukommen. Und wie bedient man die seitlichen Tasten, ohne versehentlich links zu klicken?
  • Kann man Programme mittels Keyboard-Shortcuts starten? Unter KDE habe ich auf Windows-X mein xterm definiert, ich dachte, wenn ich Apfel-X drücke könnte doch das Terminal aufgehen.
  • Wie finde ich heraus, wieviel Speicher in der Kiste steckt? top zeigt mir soetwas an: PhysMem: 144M wired, 247M active, 659M inactive, 1.03G used, 482M free Bedeuted das used + free = 1.5G? Das wäre zumindest das, was in „Über diesen Apfel“ angezeigt wird, allerdings habe ich nur 1G bestellt…
  • Die Tastatur… zur Position des @-Symbols und dass man mit der gewohnten Tastenkombination das laufende Programm beendet, sage ich mal gar nichts. Aber wo bitte ist die Backslash-Taste? Wie kann ich in Text-Controls ein Wort vor und zurück springen? Die unter „normalen“ Oberflächen wirksame Tastenkombination CTRL-Links / CTRL-Rechts springt an den Zeilenanfang / das Zeilenende. Pos1 und Ende funktionieren auch nicht so wie ich mir das vorstelle…
  • Und wie zur Hölle mountet man Netzlaufwerke dauerhaft? Also so, dass sie Ruhezustand und Reboot überleben? Wie kann ich nicht-browsable SMB-Shares mounten (d.h. ich muss den Sharenamen selbst angeben)? Wie kann man NFS-Shares mounten?

MP3s durchnummerieren

Was mich beim ipod extrem nervt, ist die Sortierung der Songs. Jeder andere Player sortiert im Zweifelsfall die Songs in einem Verzeichnis nach dem Dateinamen. Apple hält sich für besonders schlau und sortiert, wenn keine Tracknummer im ID3-Feld steht, statt dessen nach dem Songtitel. Ahrg, da nützt es natürlich nichts, wenn die Dateien „Track01.mp3“ bis „Track99.mp3“ benannt sind 🙁
Ok, was tun, wenn man 1300 Alben auf der Platte hat, von denen bei etwa der Hälfte die Tracknummer fehlen? Easytag ist zwar gut, aber so viel Zeit habe ich dann doch nicht 😉 Ich habe mir ein olles PHP-Script hingefrickelt, bis es tat, was es sollte. Wer mehr Komfort möchte, darf es schöner machen 😉
„MP3s durchnummerieren“ weiterlesen

Auf die Ohren

Aufgrund von erhöhter Reisetätigkeit eine tragbare Aufwertung des Selbstbewusstseins mit Tonausgabe erworben. Man erträgt das Gelaber der mitreisenden Chirurgen über neue Minimal-Invasivtechniken einfach nicht mehr. Paketverfolgung im Internet ist ein Fluch. CTRL-R alle 20 Minuten, zwei Tage lang. Ich hätte natürlich auch einfach in den nächsten Geizmarkt gehen können, fällt mir hinterher ein. Dann steht plötzlich ‚delivered‘ in der Statusspalte und ich denke „Hoppla“. Tatsächlich liegt ein Paket für mich auf dem Tisch.
Erste Ernüchterung beim Auspacken. „Don’t steal music“ prangt mir entgegen. Ich dachte Kunden werden nur bei der Konkurrenz kriminalisiert. iTunes ist ein Krampf, kein Drag and Drop, soll ich etwa 80 Ordner von Hand hinzufügen? Herrjeh… Zum Glück gibt es genug Alternativen, sogar einen passenden IO slave für KDE. Positiv: das Gerät ist exakt so groß wie mein Telefon und passt locker in die Hosentasche.