Posts Tagged ‘Python’

Sonntag, 10. März 2019

pip: pkgcairo: Fehlende Debian-Pakete

Damit pip das Paket pkgcairo installieren kann, müssen auf dem Debian-System folgende Pakete installiert sein:

# apt-get install libcairo2 libcairo2-dev libjpeg-dev libgif-dev python-cairo

Leider halfen diese Pakete auf meinem System auch nicht viel, denn:

...
running build_ext
pycairo: new API
error: pycairo >= 1.11.1 required, 1.8.8 found.

Tags: , , , ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 10. März 2019

Xiaomi Yeelight Lightstrip API für Dritt-Apps öffnen

Unsere Wohnung zählt insgesamt sechs Xiaomi Yeelight Lightstrips, die ich mir über AliExpress für je umgerechnet 25 CHF/Stück bestellt habe. Verglichen mit den „westlichen“ Angeboten wie bspw. dem Philips Hue Lightstrips+ Basispaket für 75 CHF ein Schnäppchen.

Natürlich gibt es zwei Nachteile: Die Dinger können nicht nativ in Apple Homekit integriert und damit gesteuert werden. Und man benötigt Adapterstecker, um die Netzteile mit chinesischen/amerikanischen Steckern in Schweizer Steckdosen zu betreiben. Tolerierbar (die Adapterstecker kriegt man ebenfalls für 50 Rappen/Stück auf AliExpress).

Kombiniert mit den Mi Jia Bewegungssensoren erhellen die LED-Streifen bei Bewegungen den Eingangsbereich (hinter der IKEA ELVARLI-Garderobe angebracht, damit die Wand schön angestrahlt wird), das Schlafzimmer, das Büro, das Badzimmer sowie das Reduit. Ein YeeLight ist hinter dem freistehenden, zweiten Kühlschrank in der Stube angebracht und wechselt seine Farben, wenn die Kühlschrank-Temperatur zu hoch ist (d.h. wenn jemand die Kühlschranktüre nicht sauber geschlossen hat). Das misst ein Mi Jia Temperatursensor im Kühlschrank.

Damit man die Streifen nicht nur über die Mi Jia respektive die Yeelight Smartphone-Apps steuern kann, sondern über eine API, muss man die LAN-Steuerung für jeden Lightstrip einzeln aktivieren:

  1. Yeelight App starten
  2. Gewünschten Lightstrip antippen
  3. Unten rechts den Eject-Button klicken
  4. Unten rechts LAN Control auswählen
  5. Den Schieberegler aktivieren
  6. Fertig

Quelle: Helping Yeti find your Yeelight: how to enable LAN control of your Yeelight devices

Ab sofort kann man die Lightstrips mit Drittsoftware steuern, oder sich mit Python-Modulen wie der YeeLight library etwas zusammenscripten.

Aktivierung in Bildern

image-8299

image-8300

image-8301

image-8302

Tags: , , , , , , ,
Labels: Home Automation

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 10. März 2019

Alle mit pip installierten Pakete aktualisieren

Leider gibt es beim Python-Paketmanager pip („Pip installs Packages“) keine Routine à la apt-get update && apt-get upgrade, um alle installierten Pakete zu aktualisieren.

Mit folgendem Befehl kommt man aber verdammt nah an diese Funktionalität dran:

# pip install -U $(pip freeze | awk '{split($0, a, "=="); print a[1]}')

Quelle: update all installed python packages with pip

Das habe ich gestern auf einem System gemacht, musste aber leider feststellen, dass pip selbst auf einem relative „einfachen“ System bei bestimmten Paketen ins Straucheln gerät. Es ist also viel Zeit und Handarbeit nötig, um sicher zu sein, dass alle Pakete aktualisiert wurden.

Tags: ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 3. Februar 2019

host löst einen lokalen Domain-Namen auf, wget und Python requests3 hingegen nicht

Vor einigen Tagen habe ich meinen Web-Server im lokalen LAN aufgeräumt und viele Services, welche über eine DynDNS-Adresse gegen das Internet zugänglich waren, deaktiviert. Ab sofort können diese Web-Services nur noch mit einer privaten FQDN à la service.server.location.domain.local aus dem LAN angesprochen werden.

Diese Anpassung führte dazu, dass mein Bluetooth-Scanner, welchen ich auf meinem Raspberry Pi betreibe, seine Scan-Resultate nicht mehr an den Web-Server übermitteln konnte. Ein Python-Script führt auf dem RPi alle 5 Minuten einen Bluetooth-Scan durch, übersetzt die Resultate ins JSON-Format und versendet die Daten dann mit requests3 per HTTP an den Web-Server.

Die Fehlermeldung, die das Script ab der Umstellung ausspuckte, lautete folgendermassen:

$ /usr/bin/python /usr/local/bin/bluelog/bluetooth-scan.py --output api
Traceback (most recent call last):
  File "/usr/local/bin/bluelog/bluetooth-scan.py", line 424, in <module>
    response = requests.post(url, json=macs, auth=HTTPDigestAuth('user','password'))
  File "/usr/lib/python2.7/dist-packages/requests/api.py", line 110, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 487, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='service.server.location.domain.local', port=80): Max retries exceeded with url: /index.php (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x75e41b10>: Failed to establish a new connection: [Errno -2] Name or service not known',))

Auf dem RPi eingeloggt funktionierte die Namensauflösung mit dem host-Kommando aber problemlos:

$ host service.server.location.domain.local
service.server.location.domain.local has address 10.1.2.3

Doch als ich die Verbindung zum Web-Server mit wget überprüfen wollte schlug auch das fehl:

$ wget http://service.server.location.domain.local/index.php
--2019-02-02 15:56:22--  http://service.server.location.domain.local/index.php
Resolving service.server.location.domain.local (service.server.location.domain.local)... failed: Name or service not known.
wget: unable to resolve host address "service.server.location.domain.local"

Herumgooglen brachte nicht die gewünschte Lösung. Deshalb griff ich zur nächstbesten Lösung und rief wget mit strace auf. Im Trace dann der entscheidende Hinweis:

$ strace wget http://service.server.location.domain.local/index.php
...
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/avahi-daemon/socket"}, 110) = 0
fcntl64(3, F_GETFL)                     = 0x2 (flags O_RDWR)
fstat64(3, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
write(3, "RESOLVE-HOSTNAME-IPV4 service.serv."..., 55) = 55
read(3, "-15 Timeout reached\n", 4096)  = 20
close(3)                                = 0
...

Was zum Teufel hat der Avahi-Daemon mit /var/run/avahi-daemon/socket im strace zu suchen? Jetzt hatte ich ausreichend Infos, um Google noch einmal zu befragen. Und siehe da:

Is Avahi running or installed? Avahi uses .local domain for its usage and puts its config in /etc/nsswitch.conf. My experience with it, using a .local domain name for my internal use is that it can be a little flaky (for lack of a better technical term), and would show behavior similar to what you are seeing.

Quelle: [SOLVED] resolver weirdness – avahi and .local

Die Lösung war im selben Beitrag gepostet:

/etc/nsswitch.conf

Aus …

...
hosts:      files mdns4_minimal [NOTFOUND=return] dns
...

… wird folgender Eintrag:

...
hosts:          files dns mdns4_minimal
...

Und seither lösen sich auch die .local-Adressen reibungslos auf.

Tags: , , , , , , , , ,
Labels: Uncategorized

Keine Kommentare | neuen Kommentar verfassen

Freitag, 23. März 2018

macOS Python kennt das Modul yaml nicht

Wird man mit folgender Fehlermeldung konfrontiert …

Traceback (most recent call last):
  File "/Users/mario/test.py", line 6, in 
    import yaml
ImportError: No module named yaml

… kann man entweder versuchen, das yaml-Modul unter der macOS Python-Installation nachzuinstallieren, oder man wechselt auf das (bei mir bereist installierte) MacPorts und verbastelt damit nicht macOS:

$ sudo port install libyaml
$ sudo port select --set python python27

Weil ich letzteren Befehl nicht auf Anhieb ausgeführt hatte, biss ich mir noch einige Minuten die Zähne aus, da das Terminal (bash) weiterhin das macOS python unter /usr/bin/python mit Version 2.7.10 ausführte und nicht dasjenige unter /opt/local/bin/python mit Version 2.7.14. Nachdem ich den port select-Befehl ausgeführt hatte, lag unter /opt/local/bin dann das Executable python (respektive der Symlink darauf) (vorher gab es dort nur python2.6, python2.7 und python3.4)

Tags: , , , ,
Labels: Apple

Keine Kommentare | neuen Kommentar verfassen

Dienstag, 13. Dezember 2016

mitmproxy: Server-Antworten umschreiben (oder: M6 Geoblocker umgehen)

Heute durfte ich für einen Kollegen in den USA eine im Internet als Stream verfügbare M6 News-Sendung rippen.

Geoblocker

Damit ich an die m3u8-Datei des Streams herankam, musste ich aber zuerst die Geo-Sperre umgehen. Die Web-Site frägt nämlich beim Aufruf (gleich zweimal) ab, in welchem Land sich der Surfer befindet. Hierzu ruft der JavaScript-Code der Web-Site folgende zwei URLs auf:

Als Antwort erhält der Browser folgenden JSON-Code zurück (IP anonymisiert):

{"offset":"2","isp":"Andreas Fink","areas":[10],"country_code":"CH","asn":"AS6775","ip":"85.195.0.0"}

Diese Antwort bewegt den Player dazu, folgende Fehlermeldung anzuzeigen:

M6 6play Geoblock
image-7099

mitmproxy installieren und konfigurieren

Der erste Task war somit klar: Ich musste mit dem quelloffenen Tool mitmproxy alle HTTP(S)-Requests meines iPads proxyifizieren und dann obige JSON-Antwort mit einer validen Antwort ersetzen, so wie sie über einen französischen ISP erfolgen würde.

mitmproxy hatte ich bereits auf meinem lokalen Linux-Server konfiguriert, weshalb ich auf dem iPad in den Netzwerkeinstellungen den Proxy nur noch erfassen musste: 10.0.X.Y:8888.

Auf dem Linux-Server startete ich mitmproxy folgendermassen:

# mitmproxy -p 8888

Als nächstes musste ich noch das mitmproxy-Zertifikat auf dem iPad installieren, damit HTTPS-Verbindungen entschlüsselt und umgeschrieben werden können. Hierzu reicht es, auf dem iPad die folgende URL aufzurufen …

mitm.it

… und den Anweisungen zu folgen (ACHTUNG: Sicherheitssensitive Personen löschen das Zertifikat nach der Verwendung wieder vom iPad. Es findet sich unter Settings > General > Profiles).

Inline-Script zum Umschreiben von Antworten

Damit ich vom Proxy-Server empfangene Antworten der M6-Server umschreiben konnte, musste ich nun noch ein mitmproxy Inline-Script einbauen, welches Aufrufe auf die oben genannten URLs abfängt und nach meinem Gusto umschreibt.

Hierzu legte ich unter /tmp/m6-geoip.py folgendes Python-Script ab:

import json

def response(context, flow):
	f = open('/tmp/mitm.log','a+')
	url = flow.request.get_url()
	f.write('URL "' + url  + '"' + "\n")

	if url.endswith('geoInfo') or url.endswith('geoInfos'):
		f.write('    matched' + "\n")
		data = json.loads(flow.response.content)
		# {"offset":"2","isp":"Andreas Fink","areas":[10],"country_code":"CH","asn":"AS6775","ip":"85.195.0.0"}
		# {"offset":"2","isp":"QSC AG","areas":[1,2,3,10,11],"country_code":"FR","asn":"AS20676","ip":"92.222.83.58"} via http://www.franceproxy.net
		#data["country_code"] = "FR"
		#jsonOut = json.dumps(data)
		jsonOut = '{"offset":"2","isp":"QSC AG","areas":[1,2,3,10,11],"country_code":"FR","asn":"AS20676","ip":"92.222.83.58"}'
		flow.response.content = jsonOut
		f.write("    " + jsonOut + "\n")
	f.close()

Tipp 1: Da selten Leute vom Himmel gefallen sind, welche ein Script auf Anhieb richtig hingekriegt haben, hilft es zum Debugging, statt mitmproxy mitmdump zu verwenden:

# mitmdump -e -p 8888 -s "/tmp/m6-geoip.py"

Bei Python-Problemen mit dem Inline-Script wird der Stack Trace und die Fehlermeldung direkt auf der Kommandozeile ausgegeben.

Tipp 2: Die Ausgabe aller URLs in eine Log-Datei resultiert in Performance-Einbussen, kann für das Debugging aber sehr nützlich sein.

Anschliessend startete ich den mitmproxy neu:

# mitmproxy -p 8888 -s "/tmp/m6-geoip.py"

JSON-Quelle

Doch von wo hatte ich den JSON-String?

Nach etwas Googeln fand ich folgenden Proxy-Service:

www.franceproxy.net

Gibt man dort eine der obigen URLs ein, erhält man als Antwort den JSON-Code, welchen ich dann auch für die Umgehung des Geoblockers verwendete.

Kostenloser Tipp an die Web-Entwickler von M6: Die Geo-Location eines Aufrufs prüft man nicht im JavaScript auf dem Client, sondern auf dem Server.

Der erneute Versuch im Web-Browser

Ich lud die Web-Seite auf dem iPad erneut — erhielt zwar eine Fehlermeldung, dass trotzdem irgendetwas schief gelaufen war, doch im mitmproxy-Log fand sich die sehnlichst gesuchte URL zur m3u8-Datei:

lb.cdn.m6web.fr

Daraus — und mit Informationen der aktuellsten Sendung, welche sich auch für Schweizer streamen lässt — leitete ich die tatsächliche URL zur Master-Playlist ab:

e112.cdn.m6web.fr

Von dieser Datei war es dann nur noch ein Katzensprung zur m3u8-Datei mit dem Stream mit der höchsten Bit-Rate:

e112.cdn.m6web.fr

Und so lud ich den Stream dann mit ffmpeg herunter:

$ ffmpeg -protocol_whitelist file,http,https,tcp,tls -i "http://e112.cdn.m6web.fr/usp/mb_sd3/2/8/1/Le-1945_c11636016_19-45-du-dimanche-11/Le-1945_c11636016_19-45-du-dimanche-11_unpnp.ism/Le-1945_c11636016_19-45-du-dimanche-11_unpnp-audio_fra=93468-video_eng=1501000.m3u8" video.mp4

Tags: , , , , , , , , ,
Labels: IT

Keine Kommentare | neuen Kommentar verfassen

Samstag, 27. August 2016

Auf dem Laptop einen Web-Server aktivieren, um Dateien auf ein iPad und iPhone herunterzuladen

In den Ferien stand ich vor dem Problem, eine ca. 50 MB grosse Datei (Teneriffa-Reiseführer von Lonely Planet) von meinem Laptop auf mein iPad und mein iPhone zu kopieren.

Ein Versand per E-Mail war aus zwei Gründen ausgeschlossen: Mein Mail-Server akzeptiert solch grosse Dateien gar nicht erst, und ausserdem verfügte unser AirBnB nur über ein popeliges ADSL-Modem mit Geschwindigkeiten um die Jahrhundertwende. Das Modem hätte mich dementsprechend zuerst mit einem extrem langen Upload zum Mail-Server und anschliessend mit einem extrem langen Download vom Mail-Server genervt. Zu guter Letzt unterstützt mein MacBook auch kein AirDrop, um Dateien per Bluetooth oder WLAN zwischen iOS und OS X-Geräten auszutauschen.

Was nun? Das standardmässige unter OS X installierte Python eilt einem zur Hilfe: Man öffnet das Terminal, navigiert zum Verzeichnis, in welcher sich die gewünschte Datei befindet, und gibt dann folgenden Befehl ein:

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

Nachdem man mittels ifconfig oder dem Network-Modul unter System Preferences die IP-Adresse des Laptops ausfindig gemacht hat, surft man auf den iOS-Geräten die folgende URL mit Safari an:

http://10.1.2.3:8000/

Ein Klick auf den Dokumentenlink, und innert weniger Sekunden wird das grosse PDF angezeigt und kann in iBooks gespeichert werden.

Python meldet auf der Kommandozeile dabei Folgendes:

10.25.9.220 - - [04/Jul/2016 10:14:18] "GET / HTTP/1.1" 200 -
10.25.9.220 - - [04/Jul/2016 10:14:20] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -
10.25.9.208 - - [04/Jul/2016 10:15:12] "GET / HTTP/1.1" 200 -
10.25.9.208 - - [04/Jul/2016 10:15:18] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -
10.25.9.235 - - [04/Jul/2016 10:15:57] "GET / HTTP/1.1" 200 -
10.25.9.235 - - [04/Jul/2016 10:16:03] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -

Tags: , , , ,
Labels: Apple, Linux

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 29. April 2015

Mit Python eine URL-enkodierte URL lesbar machen

Ein sehr nützliches kleines Script, welches ich mir aus aktuellem Bedarf zusammengeschuster habe:

import urllib.parse
urlEncoded = input("Enter URL to decode: ")

urlDecoded = urllib.parse.unquote_plus(urlEncoded)

print("")
print(urlDecoded)
print("")

elements = urlDecoded.split("?")

print("Base URL: " + elements[0])
print("")

elements = elements[1].split("&")

for element in elements:
    items = element.split("=")

    print(items[0] + ' = ')
    print('    ' + items[1])
    print("")

Tags: , , ,
Labels: Uncategorized

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 13. November 2014

Der Python-Paketmanager pip

Dann und wann programmiere ich anstelle mit PHP mit Python – wie gestern, als ich meinen Crawler websta-crawler für das (inoffizielle) Instagram-Frontend websta schrieb.

Für dieses Script greife ich auf die zwei Python-Module requests sowie BeautifulSoup zurück, welche bei einer Mac OS X-Standardinstallation nicht mit dabei sind.

Ein einfacher Weg, diese Module nachzurüsten, ist pip, eine Art Paketmanager für Python. Dieses Tool installiert sich folgendermassen:

$ wget https://bootstrap.pypa.io/get-pip.py

Anschliessend führt man das aus dem Internet heruntergeladene Script aus:

# python get-pip.py

Via: Installation

Sobald der Paketmanager ordnungsgemäss installiert wurde, reicht ein Einzeiler, um die zwei gewünschten Module nachzuinstallieren:

# pip install BeautifulSoup requests

Via: How to install multiple python packages at once using pip

Tags: , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 10. Juli 2014

Mit Python URLs dekodieren und JSON schön ausgeben

Ausgehend von dieser haarsträubenden URL der Credit Suisse …

https://www.credit-suisse.com/who_we_are/de/office_locator.jsp#%7B%22fs%22%3A%7B%22cid%22%3Anull%2C%22prid%22%3Anull%2C%22plid%22%3Anull%2C%22sid%22%3Anull%2C%22d%22%3A%5B0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%5D%7D%2C%22ms%22%3A%7B%22c%22%3A%7B%22lat%22%3A46.94739121310314%2C%22lng%22%3A7.44410902261734%7D%2C%22z%22%3A18%7D%2C%22mk%22%3A%7B%22id%22%3A4451%7D%2C%22is%22%3A%7B%22id%22%3A%22mapPanel%22%2C%22ps%22%3A%7B%22id%22%3A4451%2C%22sid%22%3A11613%2C%22segid%22%3Anull%2C%22d%22%3A%5B0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%5D%7D%7D%7D

… nachfolgend zwei Python Code-Snippets, mit welchen ihr die URL menschenlesbar machen könnt:

URLs dekodieren

urlClean = urllib.unquote(urlRaw).decode('utf8')

JSON schön ausgeben

json.dumps(json.loads(strJson), sort_keys=True, indent=4, separators=(',', ': '))

Tags: , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen