Montag, 4. November 2019

MQTT - ich will das testen

... und aufschreiben. Das ist alles ziemlich spannend.
Entstanden ist letztendlich ein kleiner Workshop als Trockentest - Es wird keine extra Hardware benötigt!

Testumgebung

Ein frisches (oder gut gebrauchtes) FHEM und dann
define mqtt2s MQTT2_SERVER 1883 global
attr mqtt2s room MQTT_IO
Ein Linux oder Windows mit mosquitto installiert (install mosquitto-clients oder Windows )
Dort Terminal oder CMD Fenster und folgende Zeile eingeben:
mosquitto_pub -h raspib2 -i COMPUTER -t CPU/raspib/temperature -m 22 
Was passiert? Es entsteht sofort ein Device in FHEM:
defmod MQTT2_COMPUTER MQTT2_DEVICE COMPUTER
attr MQTT2_COMPUTER IODev mqtt2s
attr MQTT2_COMPUTER readingList COMPUTER:CPU/raspib/temperature:.* temperature
attr MQTT2_COMPUTER room MQTT2_DEVICE

setstate MQTT2_COMPUTER 2019-10-21 12:57:00 temperature 22

Empfänger

Weitere Publish Befehle aktualisieren nur das Reading temperature. 

Hinweis 2023: Ich habe ein universelles Script geschrieben um Daten von einem Linux System zu einem MQTT Server zu senden.

Beispiel: Endlosschleife (Abbruch ctrl+c) die alle 30 sec die aktuelle CPU Temperatur schreibt:
while [ true ] ; do mosquitto_pub -h raspib2 -i COMPUTER -t CPU/$(hostname)/temperature -m $(($(</sys/class/thermal/thermal_zone0/temp)/1000)) ; sleep 30 ; done
Der wird sogar geloggt, autocreate hat gleich ein FileLog mit angelegt.
Diese Device bedient jetzt eine wesentliche Funktion: den MQTT Empfänger.

Hinweis: Die autocreate Funktion erzeugt ein Device mit der CID (COMPUTER).
Wird die while Schleife auf einem anderen Computer gestartet, ändert sich die Topic Liste (wegen CPU/$hostname). Dadurch wird die readingList ergänzt aber das Reading bleibt das Gleiche (temperature) d.h. alle Werte von unterschiedlichen Quellen kommen in das gleiche Reading! Weiter unten zeige ich wie man das manuell ändert.

Man wird in der Praxis nicht umhin kommen die automatisch erzeugten MQTT_Geräte manuell zu bearbeiten! (Stand November 2019)

Monitor

Ich kann die Nachrichten auch ganz andere Stelle sehen, z.B. in einem Terminal. Dort einfach als Subscriber für alle Topics registrieren:
mosquitto_sub -h raspib2 -v -t '#'
Damit wird jede Nachricht vom Terminal 1 an den MQTT Server auch in Terminal 2 empfangen. Im mqtt2s sieht man im reading nrclients die Verbindung. Das eigene Device wird dort nicht angezeigt.

Sender

Als nächstes will ich von dem Device etwas publishen (eigentlich ein unnützer Versuch)
attr MQTT2_COMPUTER setList off:noArg cmnd/raspib/POWER off\
on:noArg cmnd/raspib/POWER on
Damit bekomme ich im Device ein on und off "Button" und sehe in meinem Terminal 2 zwischen den Temperaturen die ankommende Nachricht. Die aber jetzt nichts bewirkt.
CPU/raspib/temperature 53
cmnd/raspib/POWER on
cmnd/raspib/POWER off
CPU/raspib/temperature 52

Gerät steuern

Versuch: Simulation und Steuerung eines externen mqtt Gerätes
Ich erzeuge in einer zweiten FHEM Instanz ein Gerät (MQTT2_DEVICE) und einen IO (MQTT2_CLIENT).
Über die readingList will ich die Nachricht POWER (von dem Publish Versuch eben) in das Reading state schreiben:
define mqtt2c MQTT2_CLIENT raspib2:1883
attr mqtt2c room MQTT_IO

define MQTT2_Test MQTT2_DEVICE Test
attr MQTT2_Test IODev mqtt2c
attr MQTT2_Test readingList cmnd/raspib/POWER:.* state
attr MQTT2_Test room MQTT2_DEVICE
Ergebnis: Ich kann jetzt sowohl von FHEM (MQTT2_COMPUTER) als auch von extern das Gerät MQTT2_Test schalten:
mosquitto_pub -h raspib2 -t cmnd/raspib/POWER -m off
Damit das Gerät sich selbst "steuern" kann und mqtt Nachrichten erzeugt erweitere ich es noch um die setList:
attr MQTT2_Test setList off:noArg cmnd/raspib/POWER off\
on:noArg cmnd/raspib/POWER on
Funktion
  • Das ursprünglich automatische Gerät "COMPUTER" kann das Gerät "Test" in der zweiten Instanz steuern.
  • Über eine separate Publish Nachricht kann das Gerät "Test" ebenfalls gesteuert werden 
  • Das Gerät COMPUTER reagiert selbst nicht auf die Nachrichten des Gerätes Test oder von "außen". 
Mit dem attr mqtt2s rePublish 1 oder mit einem zusätzlichen IO (MQTT2_CLIENT) funktioniert dies auch in der ersten FHEM Instanz.
Ich bin nicht sicher ob es einen gutem Grund(Schleifen?) gibt, die eigenen Publish-Nachrichten nicht per default an die eigenen Devices zu verteilen.

Erweiterung des Versuches:
Man kopiert die komplette Definition von "Test" inklusiver der Definition mqtt2c in die erste Instanz.
Ergebnis: Beide FHEM Instanz haben jetzt ein Gerät "Test" welches synchron schaltet.
Zusätzlich können beide Geräte von "außen" und über das Gerät "COMPUTER" gesteuert werden. Durch Manipulation der IOs mit disable 1/0 und beim mqtt2s mit rePublish 1/0 kann man das Verhalten und die Auswirkungen gut testen.

MQTT über Internet

Steve hat hier eine Übersicht über kostenfreie MQTT Broker erstellt.
Bei myqtthub.com gibt es einen "open Plan" der kostenfrei einen MQTT Broker bietet (Oktober 2020).
Bei cloudmqtt.com gab es mal die "cute cat" kostenfrei. 
Nachtrag Juli 2020:  Mein Account funktioniert weiter, obwohl das Angebot nicht mehr existiert:
  • den Zugang "normal", 
  • SSL/TLS, Websocket und 
  • API Zugriff! 
  • 5 User Connections und 10 kbit/s Datentransfer Volumen. 
Achtung: Die CID muss beim Zugriff auf cloudmqtt einmalig sein, eine zweite Verbindung mit gleicher CID beendet die Erste!
So bekommt die Testumgebung (2. Instanz) einen weiteren IO.
define mqtt2Cloud MQTT2_CLIENT xxxxxx.cloudmqtt.com:21234
attr mqtt2Cloud SSL 1
attr mqtt2Cloud room MQTT_IO
attr mqtt2Cloud username uuuuuuu
set mqtt2Cloud password pppppppppppp
Um die Verbindung zum cloudmqtt zu testen habe ich mosquitto_pub oder auch ein Android App MyMQTT ausprobiert. Die Android App kann leider keine SSL Verbindung.
Hinweis: Will man mit mosquitto eine SSL Verbindung machen, muss man die Option --insecure verwenden?! (leider weiß ich nicht warum)
mosquitto_sub -h xxxxxx.cloudmqtt.com -p 21234 -u uuuuuuu -P pppppppppppp -v -t '#' --capath /etc/ssl/certs --insecure
Um eine Reaktion im System zu erhalten, habe ich noch ein MQTT2_Test1 Device in der 2. Instanz erstellt:
define MQTT2_Test1 MQTT2_DEVICE Test1
attr MQTT2_Test1 IODev mqtt2Cloud
attr MQTT2_Test1 readingList mobil/POWER:.* state
attr MQTT2_Test1 room MQTT2_DEVICE
attr MQTT2_Test1 setList off:noArg mobil/POWER off\
on:noArg mobil/POWER on
Das reagiert jetzt auf den Topic mobil/POWER (analog zu dem vorhanden MQTT2_Test) Device und ist "mobil" steuerbar.

Nach kurzer Zeit ist mir etwas aufgefallen:
Erweitert man die readingList um den topic vom Gerät MQTT2_Test:
attr MQTT2_Test1 readingList mobil/POWER:.* state\
cmnd/raspib/POWER:.* Teststate
wird das Reading Teststate analog zum Reading state von Gerät MQTT2_Test aktualisiert.
  • Mehr noch: Ich kann den topic cmnd/raspib/POWER über den cloudmqtt Server publishen und das Gerät Test in der 2. Instanz schalten. Das attr IODev muss dazu nicht verändert werden: MQTT2_Test hat IODev mqttc und 
  • MQTT2_Test1 hat IODev mqttCloud! 
  • Das IODev hat ausgehend Bedeutung, eingehend verarbeiten die MQTT2_DEVICEs nur den Topic!

Brücke schlagen

Ich will den Versuch erweitern und Werte zwischen FHEM Instanzen übertragen, die gar nicht aus dem mqtt Umfeld kommen.
Dazu erzeuge ich in meiner zweiten Instanz ein paar "manuelle Temperaturfühler":
define fuehler1 dummy
attr fuehler1 readingList temperature humidity
attr fuehler1 room Fühler
attr fuehler1 setList temperature:25,25.5,26,26.5,27,27.5,28 humidity:50,55,60,65,70,75,80
attr fuehler1 userReadings state {my $temp=ReadingsVal($name,"temperature",99);;my $hum=ReadingsVal($name,"humidity",99);;sprintf "T: $temp H: $hum"}
Zur Übertragung nehme ich ein notify (die Idee ist von hier). Das notify macht folgendes:
  • es reagiert auf Events der Geräte fuehler1 oder fuehler2 usw. sowie deren Readings temperature und humidity
  • es verwendet set magic
  • es befreit den Readingnamen im Event vom ":" (wobei ich nicht sicher bin ob das wirklich sein muss). Die Idee stammt von hier.
define n_publish3 notify fuehler.:(temperature:|humidity:).* set mqtt2c publish -r home/states/$NAME/{((split(":","$EVTPART0"))[0])} $EVTPART1
attr n_publish3 room Fühler
In der ersten Instanz ensteht jetzt durch autocreate eine Art Sammeldevice MQTT2_mqtt2c.
Aus diesem erzeugt man sich die passenden Devices. Wichtig hier ist die Anpassung der Topic "Kette"
define fuehler1 MQTT2_DEVICE mqtt2c
attr fuehler1 readingList home/states/fuehler1/temperature:.* temperature\
home/states/fuehler1/humidity:.* humidity
attr fuehler1 room Fühler
attr fuehler1 userReadings state {my $temp=ReadingsVal($name,"temperature",99);;my $hum=ReadingsVal($name,"humidity",99);;sprintf "T: $temp H: $hum"}

Modifikation

Die attr readingList wird schnell unübersichtlich und schwierig zu warten. Mit nur zwei Modifikationen stellt man die Übertragung auf das JSON Format um.

  • Die Definition der mqtt Geräte wird einheitlicher und einfacher.
  • Man kann auch leicht mehrere Readings in einer Nachricht übertragen.

Erste Instanz
Die readingList reagiert so auf alle Nachrichten die mit dem Topic home/states/ beginnen und wo im Topic der eigene Gerätename vorkommt.
attr fuehler. readingList home/states/.* { if ($TOPIC=~m/$NAME/) { json2nameValue($EVENT) } }
Zweite Instanz
Das notify erzeugt ab sofort Nachrichten im JSON Format.
defmod n_publish3 notify fuehler.:(temperature:|humidity:).* set mqtt2c publish -r home/states/$NAME { "{((split(":","$EVTPART0"))[0])}": $EVTPART1 }

Alle Readings auslesen

Ich habe mal noch zwei Codezeilen gebaut, die entweder alle Readings eines Gerätes oder bestimmte Readings (Array) als JSON String ausgeben. Dieser Code ist zum Test in der Kommandozeile gedacht!
# Für Kommandozeile - alle Readings
{my $d="fuehler1";;my $hash = $defs{$d};;my $readings = $hash->{READINGS};;my $message="{ ";;foreach my $a ( keys %{$readings} ) {my $val=ReadingsVal($d,$a,"error");;$message .= toJSON($a)." : ".toJSON($val)." ," };;chop($message);;$message.="}"}

# Für Kommandozeile - nur zwei Readings
{my $d="fuehler1";;my @readings = ("temperature","humidity");;my $message="{ ";;foreach my $a ( @readings) {my $val=ReadingsVal($d,$a,"error");;$message .= toJSON($a)." : ".toJSON($val)." ," };;chop($message);;$message.="}"}
Will man im obigen set ... publish eine JSON formatierte Nachricht mit allen Readings des triggernden Gerätes absetzen, sieht das wie folgt aus (Dieser Code ist für die DEF!):
{(my $d="$NAME";;my $hash = $defs{$d};;my $readings = $hash->{READINGS};;my $message="{ ";;foreach my $a ( keys %{$readings} ) {$message .= toJSON($a)." : ".toJSON(ReadingsVal($d,$a,"error"))." ," };;chop($message);;$message.="}")}
Ich habe bewusst auf Formatierung verzichtet, in der DEF kann man den Code "lesbarer" formatieren.
AchtungDie FHEM Kommandozeile und set magic haben gegenüber dem normalen Perl Code immer ein paar Besonderheiten. -> ;; {(...)} "$NAME"

Tipp:

Nimmt man als MQTT Server einen Server in der Cloud, kann man damit die FHEM Instanzen ziemlich simpel übers Internet koppeln, ohne irgendeine Portfreigabe!

Allgemeine Brücke

Das Modul MQTT_GENERIC_BRIDGE scheint für die Kopplung der MQTT- und Nicht-MQTT Welt designed zu sein - eigentlich wollte ich dies als nächstes testen. Die Installation ist deutlich aufwendiger, deswegen sehe ich erstmal davon ab.

Ganz verrückt

Man kann aber auch mit einem kleinen  Shell Script mqtt Nachrichten auf System Ebene verarbeiten.

Notizen und Infos

Die mosquitto Windows Version installiert Server und Clients in einem Setup, man kann den Service aber "aushaken". Unter Linux installiert das Paket mosquitto den Server und mosquitto-clients nur die Client Tools.

Achtung: Alle MQTT Server laufen per default auf Port 1883! (Auch Mosquitto Server und MQTT2_SERVER) Es kann nur einen geben!

Gibt man bei mosquitto_pub keine CID an (Option -i) dann wird dies per default so gesetzt: "Defaults to mosquitto_pub_ appended with the process id." MQTT2_DEVICE ignoriert diese CID und legt dafür kein neues Device an!

Topic: Nach ersten Versuchen habe ich irgendwie gelesen, man soll bei den Topics keine führenden Slash's nehmen. Dadurch entsteht eine leere Ebene (bad practise).
Man kann jeden beliebigen Topic monitoren, das # gibt alle weiteren Level zurück. Das # allein liefert alle Topics, damit die Shell das nicht als Kommentar interpretiert muss man '#' schreiben!

Von steves-internet-guide habe ich mir auch ein paar Anregungen geholt.

Mittwoch, 30. Oktober 2019

File Log - und gut?

Ich suche schon lange einen Anfang um vom FileLog auf eine Datenbank umzusteigen. Es soll ja Vorteile bringen ...
Das sind einfach ein paar Notizen, eine Anleitung wird das so erstmal nicht werden.

Ein paar Gedankenstriche

  • Ich hätte Bedenken eine SQL Datenbank auf einer SD Card zu betreiben.
  • SQLite ist eine Datei basierte, lokale Datenbank, es ist kein Datenbankserver.
  • MySql ist ein Datenbankserver, der Zugriff kann auch Remote erfolgen, man könnte die Server-Instanzen auf mehrere Maschinen aufteilen.
  • Einzelne Textdateien können beim Stromausfall unbrauchbar werden, bei einem Datenbankserver kann die gesamte Datenbank unbrauchbar werden.
  • Backup muss anders gemacht werden, einfach alle Dateien eines laufenden DB Servers sichern ist nicht sinnvoll.
  • Die Erzeugung von SVG-Plots ist aus meiner Sicht derzeit schwieriger als aus FileLogs und hat Stolpersteine.

Um in das Thema einzusteigen, habe ich MariaDB installiert, ein DBLog und ein FileLogConvert Device in FHEM angelegt und erstmal geschaut wie es sich mit einem importierten Jahreslog von 2018 mit der Temperatur und Luftfeuchte meines Aussensensors so arbeiten lässt.
Ich konnte die Temperatur plotten, die Luftfeuchte funktionierte nicht. Obwohl ich dachte, ich hätte die Erklärungen im Wiki für den Umstieg verstanden. Hatte ich - aber meine Daten waren Murks.

Plot erzeugen

Was habe ich beim Erzeugen eines Plot bisher verstanden und für mich als Handlungsgrundlage festgelegt habe:
Das Anlegen eines Plots geht quasi los wie immer:
Am DbLog Device gibt es oben einen Link: Create SVG Plot from DbLog
Im Menüpunkt "Diagram label, Source" wählt man als Quelle sein DbLog Device.
Dahinter kommt die Wertzuweisung in der Form (für den Fall das man nicht nur ein Reading mit einem Wert pro Zeile geloggt hat).
<Device>:<Reading>:::$val=~s/<regex>/$1/eg
$val ist die interne Variable die letztendlich den Wert für diese Plotzeile enthält (z.B. die Temperatur)
Auf $val wird ein regex angewendet (=~) welches mit suchen (<regex>) und ersetzen ($1) den Inhalt von $val verändert. Trifft das regex nicht zu, bleibt $val unverändert!
$1 (oder auch $2 usw) enthält dabei den "Groupmatch", also normal einen Teilstring.

Match

Dezimalzahlen mit Punkt kann man so als Gruppe erkennen: ([-\d.]+) -> erkennt eine Zeichengruppe aus "- . und Ziffer" zwischen 1 und unendlich mal. (* erkennt zwischen 0 und unendlich mal, Punkt ist hier wirklich der Punkt und nicht ein beliebiges Zeichen)
Ein "T: " am Beginn eines Wertes kann man so erkennen: ^T:\s -> Anfang der Zeichenkette, ein T, ein : und ein Whitespace.
Einen Trenner zwischendrin kann man so erkennen: .H:\s -> Ein Zeichen, ein H ein : und ein Whitespace
Mein geloggtes state Reading sieht so aus: T: 20.7 H: 69 und mit dem regex   ^T:\s([-\d.]+).H:\s([-\d.]+) habe ich den Zahlenwert hinter T in $1 und den Zahlenwert für H in $2.
Nach diesem Schema ließen sich jetzt beliebige Kolonnen analysieren. Anstatt in jeder Zeile die Klammern zu versetzen und immer eine Ersetzung mit $1 zu machen, finde ich es übersichtlicher jede Zeile mit gleichen (allen) Klammern auszustatten und von Zeile zu Zeile $1 auf $n zu wechseln.

Resultat

Zeile in der Datenbank
select * from history where (TIMESTAMP like "2019-08-19 10:25:21");

+---------------------+--------------+-------+----------------------+---------+---------------+------+
| TIMESTAMP           | DEVICE       | TYPE  | EVENT                | READING | VALUE         | UNIT |
+---------------------+--------------+-------+----------------------+---------+---------------+------+
| 2019-08-19 10:25:21 | SensorAussen | DUMMY | state: T: 20.7 H: 69 | state   | T: 20.7 H: 69 |      |
| 2019-08-19 10:25:21 | SensorAussen | DUMMY | tempMax: 30.3        | tempMax | 30.3          |      |
| 2019-08-19 10:25:21 | SensorAussen | DUMMY | tempMin: 18.6        | tempMin | 18.6          |      |
+---------------------+--------------+-------+----------------------+---------+---------------+------+

Zeilen im SVG Plot
SensorAussen:state:::$val=~s/^T:\s([-\d.]+).*H:\s([-\d.]+)/$1/eg
SensorAussen:state:::$val=~s/^T:\s([-\d.]+).*H:\s([-\d.]+)/$2/eg

Für die Zeilen wo nur ein Wert in Value steht sieht die Sache viel einfacher aus:
SensorAussen:tempMax::

Fragen

Was der Wert an dritter Stelle (::0:) bewirkt den man laut Wiki hinschreiben kann? - ist mir nicht klar.
Auch ist nicht klar, warum bei der Angabe von $val= noch ein vierter Trenner ":" sein muss.
Device:Reading:?:?:$val= ...
Der Umgang mit Platzhalter <SPEC1> (attr plotfunction) oder mit attr plotReplace ist mir auch noch nicht klar. Es würde SVG gplot Dateien universell nutzbar machen.

Achtung

Wenn die current Tabelle gefüllt ist, steht der Fenster Device:Reading nicht zum Schreiben zur Verfügung. Man kann dann nur "Pärchen" aus der Klappliste auswählen. Wenn man dann speichert wird jede Zeile die man vorher mit :$val= eingetragen hat gelöscht!
Will man den Inhalt der current löschen geht das mit einem SQL Statement:
delete from current;

Fazit

Es ist aufwendig und der Nutzen scheint mir derzeit gering. Ich muss mich noch weiter damit beschäftigen

Dienstag, 8. Oktober 2019

schaltbare Steckdose auf Wlan

Vorbemerkung:
An der hier erwähnten Software tuya-convert und auch an der MQTT(2) Implementierung in FHEM wird quasi ständig gearbeitet. Konkrete Befehle usw. können sich mittlerweile geändert haben!

Der Markt ist förmlich geflutet von diesen "Plugs". Alle arbeiten irgendwie ähnlich, haben einen ESP Chip als "Herz" mit einer Software die meist über irgendeine Cloud, mit einer App auf dem Smartphone "spricht". Und dann kann "Oma" vom Kreuzfahrtschiff aus, zu Hause das Licht an und aus machen. Viele dieser Geräte bedienen sich der Cloud von Tuya und diese kann man leicht umprogrammieren und sie damit "Cloud Frei" betreiben.
Wie verbindet man diese Dinger jetzt mit FHEM? Ein Zauberwort ist MQTT - mal wirklich so etwas wie der Standard in der Automatisierung.

Ich habe vier Gerätetypen ausprobiert:
  • Gosund SP111 (2500 Watt und 3450 Watt Version)
  • Shelly Plug S
  • Gosund SP1
Die ersten Beiden sind vom äußeren her besonders schick und klein, die Dritte ist größer und kann 16 Ampere schalten. So groß und klobig wie meine "alten" Homematic Dosen sind sie alle nicht. Alle Dosen können die angeschlossene Leistung messen, was für Automatisierungsaufgaben sehr hilfreich ist!

Richtige Firmware flashen

Achtung: Für tuya-convert empfehle ich eine extra SD Card /extra System zu nehmen! Nicht einfach ins produktive System installieren!
Es gibt eine neuer Version, die arbeitet etwas anders als hier beschrieben.

Viele Wlan Dosen können ohne sie zu öffnen nach der Anleitung im obigen Link geflashed werden. Man holt sich zunächst die aktuelle Software vom GitHub und startet das Script
git clone https://github.com/ct-Open-Source/tuya-convert
cd tuya-convert
sudo ./install_prereq.sh

danach kann der Flashvorgang gestartet werden
sudo ./start_flash.sh
Man muss den ersten Dialog mit yes + enter beantworten. Danach muss man mit einen Gerät (Android oder Windows PC, alles andere geht wohl nicht) eine Wlan Verbindung zu vtrust-flash machen und behalten!
Jetzt startet man die Dose, sie muss schnell blinken! Nach ein paar Sekunden sollte ein Download Dialog zu sehen sein. Wenn es beim ersten Mal nicht funktioniert, lohnt es sich mehrfach zu probieren!
Ist der Vorgang komplett abgeschlossen, ist nur eine Übergangsfirmware auf der Dose, hier käme man noch zurück oder flashed jetzt tasmota. Der erste Befehl zeigt nur die Bereitschaft, der zweite Befehl flashed Tasmota Basic.
curl http://10.42.42.42/flash2
curl http://10.42.42.42/flash3

Nach einiger Zeit muss sich ein neues Wlan melden sonoff-1234.
Damit beendet man den Vorgang:
sudo ./stop_flash.sh

tuya convert flashed eine Sonoff/Tasmota Basic Version 6.6.0.7(digiMIN). Ich habe festgestellt, dass vor allem bez. der Messung das Standard Image wesentlich besser läuft. Das kann man nach dem ersten Start in der Oberfläche des ESP OTA flashen.
Achtung: Aktuell (ab Version 7?) haben die Entwickler alles umbenannt: sonoff.bin heisst jetzt tasmota.bin. Man muss also die url ändern:

http://thehackbox.org/tasmota/release/tasmota.bin
# oder eine andere Sprache
http://thehackbox.org/tasmota/release/tasmota-DE.bin

Danach Werksreset machen und neu konfigurieren! Jetzt heisst das Gerät auch nicht mehr sonoff-1234 sondern tasmota-1234

Wenn es gar nicht funktioniert: öffnen, Drähte anlöten und mit USB-Serial Wandler flashen.
zu offenem sonoff-1234 verbinden (die vierstellige Zahl merken) und (falls es nicht automatisch funktioniert) zu 192.168.4.1 verbinden. Dort Wlan konfigurieren.

Konfiguration der Steckdose an sich per eigener Weboberfläche

Tasmota

Eigentlich muss man primär nur 2 (3) Punkte konfigurieren.

1. Menüpunkt: Configure Module
Gosund SP1 - direkt als SP1 vorhanden
Gosund SP111 - als BlitzWolf SHP konfigurieren
SP111 neue Version (3450 Watt) - Für diese Dose braucht man ein Template!

1.1 Menüpunkte: Configure Other / Template
Ich habe hier ein Template gefunden:
Rot Wlan, Blau Status
{"NAME":"Gosund SP111 2","GPIO":[56,0,57,0,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":18}
Rot Status, Blau Wlan
{"NAME":"Gosund SP111 2","GPIO":[57,0,56,0,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45}

2. Menüpunkt: Configure MQTT
Server, Port, User, Password einrichten und Haken bei Passwort setzen!

Shelly Plug

Im Menüpunkt Internet&Security/Developer "enable action excution via mqtt" aktivieren und
  • Benutzer/Passwort
  • Server:Port 
eintragen.

MQTT in FHEM

Ist absolut easy. Man definiert
define mqtt2s MQTT2_SERVER 1883 global
und das war es schon. Für die Sicherheit sollte noch ein allowed definiert werden.
define allowedMqtt allowed
attr allowedMqtt validFor mqtt2s
set allowedMqtt basicAuth username password
Die Steckdosen werden, nachdem sie etwas gesendet haben, automatisch angelegt (Raum MQTT2_Device), man muss nur etwas warten. Die Meldung der Dose beim mqtt Server bewirkt ein autocreate.
Um die Dose schalten zu können braucht man lediglich ein passendes Template anzuwenden.
Dort passieren laufen noch Entwicklungen, deswegen fällt die Beschreibung hier nur kurz aus. Beispiel:
set MQTT2_DVES_xxxxx attrTemplate tasmota_basic

Empfehlung

Wer es klein und hübsch haben will und keine große Leistung braucht, nimmt den Shelly Plug S. Den braucht man nicht umflashen, der misst zuverlässig die Leistung, lässt sich leicht über die eigene Firmware konfigurieren und in MQTT einbinden.
Für größere Leistung nimmt man den Gosund SP1 (gibt es baugleich unter anderen Bezeichnungen). Der misst zuverlässig und ist mit Tasmota Software gut konfigurierbar.
Der Gosund SP111 ist (mit Tasmota) aus meiner Sicht nicht so doll. Die Messwerte schwanken in einem Bereich +- 10%.
Es gibt von der SP111 eine Version 3500 Watt. Die hat eine andere Platine, kann aber auch OTA geflashed werden.
Alle Stecker haben einen Eigenverbrauch:
ca. Angabe in Watt (aus - ein)
  • 0,5-0,8 (Shelly) 
  • 0,3-0,6 (SP111)  
  • 0,3-1,1 (SP1)
Man kann bei allen drei Dosen festlegen wie das Verhalten nach einem Stromausfall ist. Bei Tasmota ist es per default so wie vor dem Stromausfall. Bei dem Shelly ist es per default aus.
Alle drei Dosen können bei einer Überlast abschalten.
Der Shelly Plug S kann auch bei einer überhöhten Eigen-Temperatur abschalten.
Es lässt sich eine maximale Einschaltzeit konfigurieren, bei der egal ist ob lokal oder über MQTT geschaltet wurde.

Tasmota

In der Weboberfläche von Tasmota lassen sich so grundlegende Sachen einstellen. Viele interne Dinge, so zum Beispiel auch Messwerte kalibrieren oder Schwellen für die sofortige Datenübertragung festlegen passiert in der "Konsole", die erreicht man aus dem Hauptmenü und die Kommandos findet man auf der Tasmota Docu Seite unter Commands. Leider ändert sich diese Seite ständig, also notfalls auf der Hauptseite nach Doc und commands suchen.

Beispiele Konsole
Durch Eingabe des Kommandos an sich ohne Parameter bekommt man den momentanen Wert. Mit Parameter setzt man den Wert neu.
TelePeriod 60
PowerDelta 101

ToDo?

Code Block

Mittwoch, 25. September 2019

Windows Datensicherung mit Bordmitteln auf USB Stick

Achtung!
Ich möchte davor warnen, capture-image in einem normalen Windows zu starten!
Bei den Versuchen eine andere Systemplatte einfach mit Hilfe meines Notebooks in ein Wim File zu sichern, habe ich leider die Erfahrung gemacht, dass entweder der Vorgang in vielen Stunden ohne Fortschritt oder nach einem Abbruch die zu sichernde Partition zerstört ist!
Der Befehl "dism /capture-image ..." darf meiner Meinung nach nur unter Windows PE verwendet werden!

Grundlegende Idee

  • Seit der Version 1709 von Windows 10 kann man mit der Datenträgerverwaltung auch auf einem USB Stick mehr als eine Partition erzeugen.
  • Der Dateiversionverlauf sichert einfach, automatisch und benutzerfreundlich die Dateien auf ein externes Laufwerk.
  • Mit dism kann man ein Image des gesamten Systemlaufwerkes auf dem USB Stick (oder auf einem Netzlaufwerk) erzeugen, welches auch noch flexibel handhabbar ist.
  • Mit Windows PE oder dem Windows Setup kann man jederzeit ein Notfall System starten.

Dienstag, 30. Juli 2019

Definitionen in FHEM komplett umziehen

Ich habe mehrere System auf denen ich etwas teste und manchmal schleicht sich nach langer Zeit quasi "produktiver Betrieb" ein. Dann möchte ich gern die getestete Definition und alles was dazu gehört: Log, Logdateien, Plots usw. in das produktive System übernehmen. Da steht also die Aufgabe neben der eigentlichen Definition auch noch ein paar Dateien zu übernehmen. Ich habe mir dazu folgendes Vorgehen ausgedacht:
  1. Alle Dateien auf das Zielsystem kopieren oder das Quellsystem mounten
  2. Die wirklich benötigten Dateien an die richtige Stellen, mit den richtigen Berechtigungen, anhand der Definitionen in den FHEM Ordner kopieren.
  3. Die Definitionen vom Quell- auf das Zielsystem kopieren.
Dabei soll so viel wie möglich "automatisch" laufen. Damit ich wenig installieren und konfigurieren muss, hole ich alle potentiellen Dateien mit scp einfach pauschal lokal in ein temporäres Verzeichnis. Hat man große log Dateien - kann man auch einschränken.
host1='Hostname Instanz1'                        #Quell FHEM Instanz
tDir=fhem                                        #relativer Pfad der im lokalen Pfad (HomeDir des Users) angelegt und verwendet wird
mkdir -p $tDir/www
scp -r pi@$host1:/opt/fhem/log/ $tDir/           #kopiert den Remote Pfad log/ nach fhem/log/  
scp -r pi@$host1:/opt/fhem/www/gplot/ $tDir/www/ #kopiert den Remote Pfad www/gplot/ nach fhem/www/gplot/
Jetzt werden die wirklich notwendigen Dateien anhand der Definitionen ermittelt und lokal an die richtige Stelle mit den richtigen Berechtigungen kopiert. Ich mache das deshalb über FHEM.
Zu allen Schritten brauche ich meinen HTTP Client
wget -O fhemcl.sh https://raw.githubusercontent.com/heinz-otto/fhemcl/master/fhemcl.sh

Um die Einzeiler etwas übersichtlicher zu halten, definiere ich noch ein paar Variablen zu Beginn, $host1 und $tDir wurden im Befehlsblock oben schon definiert. In den Einzeilern wird einfach davon ausgegangen, dass der lokale Zugriff nur die Port Nummer 8083 erfordert.
inst1=$host1:8083                #URL Remote Instanz [http://[<user:password@>][<hostName>:]]<portNummer> 
rDir=$(pwd)/$tDir/               #absoluter Pfad für die (kopierten) Quelldateien
gDir='./www/gplot/'              #Pfad in dem die gplot Datei in FHEM liegen
devSpec='FILTER=NAME=.*Sensor.*' #wird in den Befehlen um TYPE= ergänzt
Man kann selbstverständlich die Quelldateien remote mit mount einbinden (z.B. nfs). Dann enthält $rDir den gemounteten Pfad.

Achtung: Die Einzeiler enthalten keine Fehlerbehandlung! Es kann also zu unvorhergesehenen Auswirkungen kommen!
Datensicherung!
Die Kopiervorgänge werden eventuell nicht oder falsch ausgeführt, können aber gleichnamige Dateien überschreiben! Die Änderungen in der cfg werden nicht automatisch gesichert, ein restart von FHEM stellt den alten Zustand wieder her.
Die devSpec sollte man im Zweifel immer testen!
bash fhemcl.sh $inst1 "list TYPE=.*:$devSpec"
Man sollte auch immer zunächst die Einzeiler ohne den letzten Ausführungsteil zur Probe laufen lassen!

Einzelne Schritte

Zunächst als "Fingerübung" die Grundlagen um aus bestimmten Definitionen, die "Sensor" im Namen haben, die Dateien zu kopieren.
  • der list Befehl erzeugt eine Tabelle aus Name und dem entsprechenden INTERNAL, von dieser wird nur die zweite Spalte benötigt,
    • für die log Datei wird mit sed alle Platzhalter %Y,%m usw. durch * ersetzt. Damit werden alle vorhandenen Log Dateien erfasst. 
    • für die gplot Datei wird mit sed noch $gDir vorangestellt und ".gplot" angehängt, 
  • der zweite sed Befehl ersetzt ./ durch den absoluten Pfad $rDir
    • Der Ausdruck ${rDir////\\/} erzeugt dabei aus dem normalen Pfadnamen einen "escaped" Pfadnamen: ersetzt also / durch \/.
  • Der tr Befehl am Ende macht aus der Zeilen- eine Leerzeichen getrennte Liste für den cp Befehl.

Die FileLog (log) Dateien

datei=$(bash fhemcl.sh $inst1 "list TYPE=FileLog:$devSpec logfile"|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' ')
bash fhemcl.sh 8083 "\"cp -n $datei ./log/\""

Die SVG (gplot) Dateien

datei=$(bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec GPLOTFILE"|awk '{print $2}'|sed "s/^/${gDir////\\/}/;s/$/.gplot/"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' ')
bash fhemcl.sh 8083 "{qx(cp -n $datei ./www/gplot/)}"
Ich verwende bewusst zwei unterschiedliche Kopierbefehle. Die erste Variante blockiert das lokale FHEM nicht, allerdings hat man keine Rückmeldung wann der Kopiervorgang beendet ist. Die zweite Variante blockiert FHEM, was bei den kleinen gplot Dateien aber nicht stören sollte.

Definition

Jetzt brauchen nur noch die Definitionen auf das lokale Zielsystem kopiert werden.
bash fhemcl.sh $inst1 "list -r TYPE=FileLog:$devSpec"|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list -r TYPE=SVG:$devSpec"|bash fhemcl.sh 8083

Verbundene Definitionen

Der list Befehl ist sehr vielseitig! Neben Gruppen von Definitionen kann man auch einzelne Geräte und deren komplette verbundenen Definitionen umziehen.
Achtung: "-R" funktioniert so richtig nur mit Device Namen ohne regExp, "-R" ist laut Doku auch "unvollkommen"!
So wird die SVG zusammen mit der zugehörigen FileLog Instanz übertragen.
bash fhemcl.sh $inst1 "list -R SVG_FileLog_SensorWG_1"|bash fhemcl.sh 8083

Abhängigkeit

Die SVG Definition und deren Funktion ist von der zugehörigen FileLog Definition abhängig. Also kann man von der SVG Definition alles notwendige ableiten. Die log Dateien anhand des SVG kopieren:
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'
Der Vorgang ist zweistufig, erst wird das LOGDEVICE aus der SVG Definition gelesen, daraus wieder ein list Befehl erzeugt und damit das log File direkt aus der Definition ausgelesen.
Nach dem obigen Schema kann man daraus die Dateiliste erstellen.
|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '
Anstatt über eine Variable ($datei) kann man die Pipe auch fortführen.
|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083
Der komplette Einzeiler ist wirklich lang.
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083

Um den list -R Befehl mit einer devSpec zu verwenden, muss man diese quasi erst auflösen.
Achtung: Wenn die devSpec nur eine Definition trifft, schlägt dieser einfache Einzeiler fehl!
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec"|sed 's/^/list -R /'|bash fhemcl.sh $inst1|bash fhemcl.sh 8083

Finale

Damit hat man 3 Schritte ausgehend von einer devSpec:
  1. gplot Dateien kopieren
  2. log Dateien kopieren
  3. SVG Definitionen und die zugehörigen FileLog Definitionen übertragen.

bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec GPLOTFILE"|awk '{print $2}'|sed "s/^/${gDir////\\/}/;s/$/.gplot/"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./www/gplot/\""|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec"|sed 's/^/list -R /'|bash fhemcl.sh $inst1|bash fhemcl.sh 8083


Donnerstag, 25. Juli 2019

rootfs auf ein anderes Laufwerk verschieben

Der Startparameter root= legt für den Linuxkernel fest wo das root FileSystem zu finden ist.
In der dann dort gelesenen /etc/fstab wird festgelegt wohin welches Laufwerk eingehängt/gemounted wird. Das gilt auch für das rootfs.
Anhand vom SingeBoardComputer Odroid HC1 will ich zeigen wie man das rootfs vom Originalstandort: 2. Partition der SD Card auf eine über Sata angeschlossene Disc (oder irgendein anders Laufwerk) verschieben kann.

Beschreibungen dazu habe ich einige gefunden, ich habe versucht sie zu verstehen und für mich so simpel wie möglich umzusetzen. Prinzipiell wird das in der Art für jeden SBC funktionieren.

Der Systemstart ist dann wie folgt: Bootsystem von SD Card lesen, rootfs auf der zweiten Disc über Sata oder USB starten.
Es gibt auch SBCs die direkt (ohne Umweg über SD Card) von einer Sata/Usb Disc booten können!
Hinweis:
Mittlerweile können einige Hardwareversionen des Raspberry Pi auch direkt von USB booten.
Dazu habe ich auch einen neuen Artikel gemacht.
Nachtrag 2022: Ich bin beim Odroid auf Armbian umgestiegen. Bei Armbian empfehle ich diesen Vorgang einfacher mit sudo armbian-config / System ... oder mit dem Script sudo nand-sata-install durchführen. Siehe dazu auch die Ergänzung am Ende des Artikels.

Donnerstag, 18. Juli 2019

Infos zur Installation von Modulen und Paketen


  • Was ist auf meinem System in der Vergangenheit installiert worden?
  • Ist ein bestimmtes Modul oder Paket schon installiert?
  • Welches Debian Paket brauch ich für ein bestimmtes Perl Modul?


Ich habe etwas herum experimentiert und dabei sind ein paar Einzeiler für die Beantwortung dieser Fragen entstanden.

Historie

Die erste Frage kann man relativ leicht beantworten, das apt System loggt nämlich alle apt-get Vorgänge sogar mit der eingegeben Kommandozeile mit.
cat /var/log/apt/history.log|grep Commandline
Besser zuordnen lässt sich das noch in der Art.
cat /var/log/apt/history.log|grep -A1 Start-Date
Beide Zeilen ergeben keine Ausgabe? Das gesuchte ist nicht dabei? Die Installation liegt schon zu lange zurück! Die Logs werden mit Log Rotate archiviert. Auch dieses Archive kann man komplett, zeitlich richtig sortiert, anzeigen/durchsuchen.
zcat $(ls -tr /var/log/apt/history.log.*.gz)|grep -A1 Start-Date

Man hat Perl Module per CPAN installiert?
Wenn man das debian Paket perl-doc installiert hat (oder noch installiert) kann man mit diesem Mammut-Einzeiler eine List der mit cpan installierten Module ausgeben (Quelle):
for M in `perldoc -t perllocal|grep Module |sed -e 's/^.*" //'`; do V=`perldoc -t perllocal|awk "/$M/{y=1;next}y" |grep VERSION |head -n 1`; printf "%30s %s\n" "$M" "$V"; done |sort

Installierte Modul und Pakete finden und listen

Wie man ermitteln kann ob ein bestimmtes Perl Modul oder debian Paket schon installiert ist habe ich hier schon mal gezeigt. Die beiden folgende Zeile liefern jeweils ein Liste aller installierter Pakete, entweder zeilenweise oder eine Zeile mit Leerzeichen getrennt. Der wichtige Schlüssel in dieser Zeile ist der String "^ii", mit diesem oder vielleicht einfacher einem weiteren grep 'suchstring' kann man die Rückgabe wesentlich beeinflussen. Beide Listentypen eignen sich für die weitere Verarbeitung.
dpkg -l |grep ^ii| awk '{ print $2 }'
dpkg -l |grep ^ii| awk '{ print $2 }'|tr -d "\r"|tr "\n" " "
Will man wissen ob ein bestimmtes Perl Modul installiert ist, kann man das einfach herausfinden. Man kann aber auch herausfinden ob und durch welches debian Paket es installiert wurde. Die zweite Version sucht nur in Paketen die -perl im Namen tragen und läuft damit wesentlich schneller.
s='Device::SerialPort'
perl -M$s -e '' 2>/dev/null &&echo "Modul $s ist vorhanden"
for i in $(dpkg -l |grep ^ii| awk '{ print $2 }'|tr -d "\r"|tr "\n" " ");do if dpkg -L $i|grep $s &> /dev/null;then echo $i enthält $s;fi;done
for i in $(dpkg -l |grep ^ii|grep '\-perl'| awk '{ print $2 }'|tr -d "\r"|tr "\n" " ");do if dpkg -L $i|grep $s &> /dev/null;then echo $i enthält $s;fi;done

Debian Pakete anhand von Perl Modulnamen finden

Es gibt gute Gründe die Installation von Perl Modulen über debian Pakete der Installation über cpan zu bevorzugen. Für mich zählt die Updatefähigkeit und die Geschwindigkeit. Man kann im Internet nach den Paketen suchen, man kann aber auch einfach die Verfügbarkeit für das lokale System testen. Dazu braucht man das Tool apt-file, welches man zuerst installieren muss. Der letzte Befehl lädt den Infocache lokal und dauert etwas.
sudo apt-get update
sudo apt-get install apt-file
sudo apt-file update
Für die folgende Suche, nutze ich den Umstand, dass in der im Paket enthaltenen Liste der Dateien auch die Doku in der Form "/usr/share/man/man3/IO::File.3perl.gz" enthalten ist. Ich ergänze also den Modulstring um "/" + "." und suche exakt danach. Da die Suche ziemlich zeitaufwendig ist, kann man auch nach mehreren Modulen in einem Vorgang suchen lassen. Der search Befehl akzeptiert mehrere Modulnamen durch Zeilenumbruch getrennt. In meinem Beispiel sind die Modulnamen in einem String durch Leerzeichen getrennt, die Trennung wird in Zeilenumbruch geändert, jede Zeile ergänzt und über stdin an den search Befehl von apt-file übergeben. Die Option -l bewirkt das nur die gefunden Pakete gelistet werden, werden mehrere der abgefragten Modulnamen in einem Paket gefunden, wird es nur einmal gelistet!
s="IO::File Digest::MD5"
echo $s|tr " " "\n"|sed 's/$/./;s/^/\//'|apt-file search -l -f -

Die Config analysieren

Mit Hilfe des Installermoduls, kann man auch eine bestehende Configdatei untersuchen und fehlende Pakete nachinstallieren. 
Voraussetzung: das neue FHEM System.
Mit einem Zusatzmodul, einer Definition sowi ein paar Zeilen Scriptcode kann man so testen ob alle Vorrausetzungen installiert sind bzw. die fehlenden Pakete installieren.
Danach backup zurückspielen und fertig. 
# System mit Suchmaschinen und Hilfsscript aufrüsten
sudo apt install apt-file libperl-prereqscanner-notquitelite-perl
sudo apt-file update
wget -O fhemcl.sh https://raw.githubusercontent.com/heinz-otto/fhemcl/master/fhemcl.sh
chmod +x fhemcl.sh

# Definition zum Testen erstellen
cat <<EOF | ./fhemcl.sh 8083
attr initialUsbCheck disable 1
define installer installer
attr installer installerMode developer
save
EOF

# Zu prüfende config Datei holen
scp pi@raspib3:/opt/fhem/fhem.cfg .
ref='/home/pi/fhem.cfg'

# Abfrage starten
s=$(./fhemcl.sh 8083 "get installer checkPrereqs $ref"|grep -oE 'installPerl.*&fwcsrf'|grep -oE '\s[a-z,A-Z,:]+\s')
packages=$(echo $s|tr " " "\n"|sed 's/$/./;s/^/\//'|apt-file search -l -f -)
echo 'debian Pakete fehlen:'
echo $packages
echo 'sudo apt install $packages' 

Gibt es noch mehr?
Oh ja, nodejs Pakete oder auch python.
Mit npm kann man so die "global" installierten Pakete listen lassen:
npm list -g --depth 0
Python ist ja Bestandteil des debian System, verwendet man zusätzliche Pakete hat man meist pip (python-pip) installiert. Python gibt es auch in der Version 3 (python3) dafür gibt es natürlich separat pip3 (python3-pip). Mit der Option -l verringert sich die Liste auf das, was man selbst installiert hat.
pip list -l
Und man kann auch schauen ob sich was neues findet.
pip list -l --outdated