Sonntag, 16. Februar 2020

Windows Server 2012 Foundation - Hyper-V Treiber installieren

Microsoft hat irgendwie umfangreich verhindert, dass man Windows Server 2012 Foundation in Hyper-V virtualisieren kann. Blöd wenn man eine produktiven Server hat, mit dem man ein paar Untersuchungen anstellen will. Virtualisierung / Hyper-V ist dafür nun mal eine Super Sache.
Es gibt einen guten Artikel, der mir sehr weiter geholfen hat.
Da die Situation nach einer Virtualisierung erstmal dramatisch ist, habe ich eine Weg gesucht und gefunden, es zu vereinfachen.
Das Hauptproblem ist die fehlende Installation der Hyper-V Treiber.

Das notwendige Paket windows6.2-hypervintegrationservices-x64.cab kann man bei Microsoft herunterladen. Die ursprüngliche existierende vmguest.iso wird nicht mehr ausgeliefert.
Für den Rest habe ich ein paar Powershell Scripts erstellt.
Zunächst brauchen wir die entpackten Dateien in einem temporären Pfad.
# cab Datei entpacken und in allen inf Dateien eine Zeile ergaenzen
$cabFile="E:\support\amd64\Windows6.2-HyperVIntegrationServices-x64.cab"
# Zielpfad frisch machen
$DestDir = (split-path $cabFile -leaf) -replace '.cab'
if (Test-Path -Path $DestDir) {rm $DestDir}
md $DestDir
# cab datei auspacken
cmd.exe /c "C:\Windows\System32\expand.exe -F:* $cabFile $DestDir"
Damit die Treiber später installiert werden können, muss die inf Datei um die eine Zeile ($rString) ergänzt werden. Zusätzlich muss "neben" der inf Datei die Datei update.cat liegen.
# replace in den inf Dateien
$fString=[regex]::Escape('[Version]') # escapen wegen der Klammer []
$rString="$&`nCatalogFile=update.cat" # Der String der gefunden wurde + NL + Text
gci -Recurse $DestDir |where {$_.name -like "*.inf"}|% {(Get-Content $_.fullname) -replace $fString, $rString| Set-Content $_.fullname;copy-item "$DestDir\update.cat"  (split-path $_.fullname)}
Die vorbereitete virtuelle Disk (VHD datei) kann man per Doppelklick einfach einbinden. Die Treiber werden dann ins Offline Image integriert.
# Treiber in offline Image integrieren
$offDir="F:"
$driverDir="\\omv1\Shares\Software\vmguest"
Add-WindowsDriver -Path $offDir -Driver $driverDir -Recurse -ForceUnsigned
Sollte was schiefgegangen sein, kann man auch alles wieder entfernen, oder sich nur vergewissern das die Treiber vorhanden sind.
# Alle Treiber wieder entfernen 
$dArray=gci -Recurse $driverDir |where {$_.name -like "*.inf"}|% {$_.name}
Get-WindowsDriver -path $offDir|% {if ($dArray -match (split-path $_.OriginalFileName -leaf)){Remove-WindowsDriver -path $offDir -Driver $_.driver}}
Zum Abschluss die VHD Datei auswerfen und die virtuelle Maschine starten. Es sind keine weiteren Arbeiten erforderlich.

Für die Virtualisierung einer produktiven Server 2012 Foundation Maschine hat man damit folgenden simplen Weg:
  1. Image ziehen, entweder offline mit dem USB Stick von hier, oder online mit disk2vhd.
  2. Das Image auf eine neue VHD anwenden, oder die VHD Datei von disk2vhd verwenden.
  3. VHD Datei als Laufwerk mounten und die Hyper-V Treiber integrieren. 
  4. VHD Datei Auswerfen und in Hyper-V verwenden.
Damit funktioniert:

  • Gen 2 Virtuelle Maschine
  • EFI Boot
  • SCSI Disk, Netzkarte usw.

Was leider bleibt:
  • Die Hyper-V Dienste (vmic...) laufen nicht. 
ToDo?
Code Block

Code Block

Mittwoch, 5. Februar 2020

Windows Core Server verwalten

Eigentlich gibt es ein simples Dokument vom Hersteller, aber das hat so viele Lücken und Fehler, ich schreibe einfach mal auf wie es kurz und knapp geht.

Verwaltung eines Core Servers (Hyper-V 2019) ohne Domäne

Um ohne lokale Konsole arbeiten zu können braucht man RDP. Das aktiviert man schnell:
  • entweder im Dialog der blauen Box von %windir%\System32\sconfig.cmd (bzw. %windir%\System32\%language%\sconfig.vbs) 
    • Dialog: 7 e 1
  • oder über ein vorhandenes Script:
cscript %WinDir%\System32\Scregedit.wsf /ar 0
REM Dieser Befehl noch für alte RDP Clients
Cscript %WinDir%\System32\Scregedit.wsf /cs 0
Man sollte den Server noch umbenennen, entweder im Dialog (2) oder
netdom renamecomputer %computername% /force /NewName:ComputerName
Danach muss man den Server neu starten!

Ab hier geht es auch Remote

Das "Netzwerkprofile" steht per default auf public, das stellt man um auf private und ermöglicht die Reaktion auf Ping. Dazu eine Powershell im "schwarzen Fenster" starten:
start Powershell
Ping funktioniert mit dem Regelset -DisplayGroup "File and Printer Sharing" (Sprachabhängig: "Datei- und Druckerfreigabe") oder "codiert" in Group.
Set-NetConnectionProfile -InterfaceIndex (Get-NetConnectionProfile).InterfaceIndex -NetworkCategory Private
Enable-NetFirewallRule -Group "@FirewallAPI.dll,-28502"

Am Client

Eine "Remote Verwaltung" funktioniert nur mit etwas Vorbereitung.

  • Der Zielcomputer muss als TrustedHost eingetragen und 
  • das Konto vom Zielcomputer muss hinterlegt werden. 

Verwirrend: Für den Hyper-V Manager muss der TrustedHost mit FQDN hinterlegt werden! Man kann sich entscheiden oder beides hinterlegen.
$server = "ComputerName"
$user = "administrator"
$pwd = Read-Host "Enter Password"
$fqdn=[System.Net.Dns]::GetHostByName($server).Hostname
Start-Service -Name winrm
Set-Item WSMan:\localhost\Client\TrustedHosts -Concatenate -Value $server
Set-Item WSMan:\localhost\Client\TrustedHosts -Concatenate -Value $fqdn
Stop-Service -Name winrm
cmdkey /add:$server /user:$user /pass:$pwd
cmdkey /add:$fqdn /user:$user /pass:$pwd

Ist alles ordentlich eingerichtet kann man eine Powershell Remotesitzung aufbauen.
Enter-PSSession -ComputerName $server

Neben Powershell und dem Hyper-V Manager funktionieren auch andere "lokale" Snap-Ins in der mmc, mir ist aber noch nicht klar nach welchem Schema was funktioniert und was nicht.
Die Hyper-V Verwaltungstools kann man besser über die grafische Schnittstelle installieren, die Powershell Schnittstelle funktioniert nur Hyper-V komplett oder gar nicht.
Mann sollte dafür die RSat Tools verwenden, oder eben alles über RDP und Powershell machen.

Informationen

Es gibt ein Tool für die Kommandozeile, welches knapp und übersichtlich die System Informationen darstellt:
systeminfo.exe
Auf dem Windows Server ist PSRemoting von Haus aus aktiviert, auf anderen Ziel Systemen kann man sie mit Enable-PSRemoting aktivieren. Ob aktiviert oder nicht lässt sich leicht von jeder Maschine aus testen:
Test-WSMan -ComputerName ComputerName
Die schon existierenden Zielsysteme kann man abfragen
Start-Service -Name winrm
(Get-Item WSMan:\localhost\Client\TrustedHosts).value
Stop-Service -Name winrm
Die existierenden Konten kann man anzeigen lassen
cmdkey /list

Auf dem Server kann man sich Profil und die aktivierten Firewall Regeln anschauen:
Get-NetConnectionProfile
Get-NetFirewallRule -DisplayGroup "Windows Remote Management"|select Name,enabled
Get-NetFirewallRule -Group "@FirewallAPI.dll,-28502"|select name,enabled
Mit der Tastenkombination Ctrl+Alt+Ende öffnet sich ein Fenster von wo aus man z.B. den Taskmanager starten kann.

ToDo Ist da noch was?
Code Block
System Volume nach der Installation auf 60GB schrumpfen und den Rest als Volume V einrichten.
$disk=Get-Partition -DriveLetter C
Resize-Partition -DiskNumber $disk.DiskNumber -PartitionNumber $disk.PartitionNumber -Size 60GB
New-Partition -DiskNumber $disk.DiskNumber -UseMaximumSize -DriveLetter v|Format-Volume

Weiteres

Insgesamt zu diesem Thema gibt es auch schon ein paar Artikel hier im Blog
Link

Sonntag, 2. Februar 2020

Altes Board von NVMe starten

M.2 SSDs sind ganz erschwinglich, einen PCIe M.2 Adapter gibt es für ein paar Euro und ein PCIe Steckplatz auf dem älteren Board ist auch noch frei. Das wäre doch was ...

Die Sache hat einen oder mehrere Haken:
  • der simple PCIe M.2 Adapter kann nur M-Key SSDs (NVMe SSDs) aufnehmen.
  • erst das EFI-BIOS der neueren Boards erkennt damit auch ein Laufwerk, die älteren kennen es bestenfalls als irgendein PCIe Gerät.
    • Die aktuellen Betriebssysteme können die NVMe SSD ohne Probleme als richtiges Laufwerk "sehen" und damit umgehen. Das geht soweit, dass man Windows installieren kann - es merkt nicht, dass das BIOS dieses Laufwerk nicht starten kann.
  • Damit kann man zwar ein System auf die neuen SSD aufspielen, aber starten kann man es nicht.
Wie so oft ist es eine Sache des Treibers: ein NVMe Treiber ist im alten BIOS nicht vorhanden.
Schade - Oder? 
Ich habe eine nicht ganz neue und simple Lösung gefunden, aber wie so oft keine aktuellen Beschreibungen. Ich musst ein paar Tage "forschen" - deswegen der Artikel.

Der "Trick" - eine kurze Umleitung


Das BIOS kann NVMe nicht direkt starten, aber Sata und USB Laufwerke. Also erstmal einen Bootloader von USB oder einer anderen EFI Partition starten, der hat den Treiber und der startet das System von NVMe. Wahrscheinlich geht das mit jedem Bootloader, wenn man weiß wie. Gefunden habe ich aber den clover efi bootloader und ein Boot Disk Utility zur einfachen Installation. Das Tool baut ohne viel Federlesen einen USB Boot Stick mit einer 200 MB EFI Bootpartition. Darin befinden sich ca. 15 MB Dateien.
Was dann nirgendwo so direkt und aktuell stand:

Die kleine Modifikation:


Es gibt in dem entstandenen Bootfähigem Laufwerk ein Verzeichnis EFI. In dem Verzeichnis <LW>:\EFI\CLOVER\drivers\UEFI müssen offenbar die notwendigen Treiber für den Start liegen.  In dem Verzeichnis <LW>:\EFI\CLOVER\drivers\off liegen offenbar die optionalen Treiber. Von dort muss man den Treiber für die NVME Disk - NvmExpressDxe.efi - in das Verzeichnis mit den notwendigen Treiber kopieren.
Das war es im Groben schon. Einfach den Stick in den PC einstecken, ihn als Boot Device konfigurieren und starten. Dann erscheint nach wenigen Sekunden ein neues Bootmenü, dort dann "Boot Microsoft EFI Boot from EFI" (oder was man starten möchte) auswählen.
Man kann den 5 sec Countdown noch verkürzen, ein Timeout 0 schaltet die GUI komplett ab und bootet sofort.
Die XML Datei kann man mit einem Texteditor bearbeiten.
<LW>\EFI\CLOVER\config.plist

Bei meinem Asrock E350M1 dauert es ca 6 sec um den Stick zu booten, die gesamte Zeit bis von der NVMe gestartet wird beträgt damit 14 sec. Ein Option Rom auf einer Sata Erweiterungskarte braucht auch ein paar Sekunden, insofern ist diese Methode kein wirklicher Nachteil.

Man braucht nicht unbedingt ein Tool um den Boot Stick zu erstellen. Man kann die gepackte ISO Datei herunterladen, entpacken und einfach wie bei einem Windows Setup Stick den Inhalt der ISO Datei auf den vorbereiten Stick kopieren.

Den Laufwerksbuchstaben kann man im laufenden Windows System mit Powershell entfernen und den Stick damit etwas verstecken.
$drive=(Get-Volume |where DriveType -eq Removable).DriveLetter
$disk=Get-Partition -DriveLetter $drive
Remove-PartitionAccessPath -DiskNumber $disk.DiskNumber -PartitionNumber $disk.PartitionNumber -AccessPath $drive":"


ToDo Ich muss noch weiter forschen....


Donnerstag, 9. Januar 2020

Passwörter in Powershell Scripts

Es gibt immer mal wieder die Notwendigkeit in automatischen Prozessen Konten (Benutzername Passwort) zu verwenden. Die sicherlich dümmste Variante ist es, sie im Script direkt zu hinterlegen. Aber wie jetzt tun?
  • Auf einem sicheren Filesystem ablegen und vor Zugriff schützen.
  • Das Passwort nur verschlüsselt ablegen. Den Key dazu im sicheren Filesystem ablegen und vor Zugriff schützen.
  • Das Script im definierten Userkontext ausführen.
  • Im Code möglichst darauf achten, dass der Speicher wieder gelöscht wird.

Das Passwort verschlüsseln

Man braucht zunächst mal einen definierten Schlüssel um zu verschlüsseln, sonst würde das Cmdlet ConvertFrom-SecureString das Benutzerkonto verwenden. das wäre nicht praktikabel.
So kann man einen zufälligen AES 256 Key (32 Byte) erzeugen:
$pwPath = ".\myPassword.txt"
$aesKeyPath = ".\aesKey.key"
$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
if (-not (Test-Path $aesKeyPath)) {Set-Content $aesKeyPath $AESKey}
Ich mag ja Einzeiler, hier eine Variante um das Passwort verschlüsselt abzulegen:
"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -Key $AESKey| Out-File $pwPath

Das Passwort entschlüsseln und verwenden

Das Passwort kann man "umgekehrt" wieder einlesen, aber dabei entsteht zunächst ein SecureString, dieser ist so nicht darstellbar und in vielen Applikationen auch nicht verwendbar..
$SecureCredential = Get-Content $pwPath | ConvertTo-SecureString -Key $AESKey
Zur Darstellung kann man das PSCredential Object verwenden (alternativ auch die .net Klasse [Runtime.InteropServices.Marshal]).
(New-Object PSCredential "username",$SecureCredential).GetNetworkCredential().Password
Dabei verlangt das PSCredential Objekt einen Benutzernamen, der mit dem Passwort aber primär gar keine Verbindung haben muss.
Entweder nimmt man hier irgendeinen "Begriff" oder man nimmt den Benutzer, den man in der weiteren Verarbeitung verwenden kann/will (z.B. Mailaccount)
$Account = (New-Object PSCredential "username",$SecureCredential).GetNetworkCredential()
$Account.Password
$Account.UserName

Das Passwort an externe Programme übergeben

Die Verwendung der Kommandozeile in Powershell kann einen manchmal verzweifeln lassen.
Einige eingebaute Alias Definitionen (Get-Alias) ersetzen bekannten Windows Kommandozeilen Befehle durch Cmdlets. Deren Aufruf Syntax ist nie 100% kompatibel!
Parameter/Argumente werden von der Powershell "analysiert" und "umgedeutet".

Im Zweifelsfall hilft nur probieren und suchen!

Leerzeichen und Sonderzeichen in den Strings machen einen das Leben zusätzlich schwer. Bestimmte Zeichen muss man mit dem backtick ` schützen (Artikel in der WinPro).
Es bieten sich ein paar Möglichkeiten zum Aufruf externer Programme an:
  • Command Shell cmd verwenden
  • Aufruf mit & Zeichen am Zeilenanfang
  • Alias setzen Set-Alias
  • Das Parsing der Argumente durch Einfügen von --% abbrechen.
Hier ein paar Beispiele für die Verwendung von Programmaufrufen in Powershell Console oder Script:
& cmd /c dir /q
set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
sz a -tzip "ArchivNameOhneEndung" "PfadName" -mem=AES256 "-p$($Account.Password)" -v2g
icacls .\Archiv1.zip.001 /grant Benutzer:`(R`)
icacls .\Archiv1.zip.001 --% /grant Benutzer:(R)
Will man die Argumente noch in eine Variable packen, wird das nochmal schwierig.
Hier hilft dann eventuell ein Array (siehe Beispiele weiter unten).

Die Dateien vor Zugriff schützen

Die Absicherung der Datei unter Windows kann mit verschiedenen Methoden erfolgen, die grafische Variante mit dem Explorer will ich nicht extra beschreiben.
Schlussendlich muss der Benutzer, der das Script ausführt die Datei lesen können!
Achtung: Der folgenden icacls Syntax funktioniert in einer Powershell Umgebung, aber besser immer icacls --% verwenden!
Beispiel: Rechte Anzeigen, Vererbung löschen, Administratoren und SYSTEM entfernen:
icacls w.txt
icacls w.txt /inheritance:d
icacls w.txt /remove Administratoren
icacls w.txt /remove SYSTEM
Mit Clear-Variable und Remove-Variable kann man sicherstellen, das Variable (mit sensiblen Inhalt) zur Laufzeit wieder gelöscht werden und nicht erst mit der "Müllabfuhr" nach Beenden des Scripts. Die Variable wird ohne $ angegeben!
Clear-Variable Account

Dinge die man nicht verstehen muss

Hier mal 3 Varianten wie man den 7Zip Befehl (Alias sz) fehlerfrei ausführen kann.
$Archiv="Archiv1"
$Pfad="c:\temp"
# Alles in einer Aufrufzeile
sz a -tzip "$Archiv" "$Pfad" -mem=AES256 "-p$($Account.Password)" -v2g
# die gesamte Aufrufzeile in eine Stringvariable verpackt
$befehl = "sz a -tzip `"$Archiv`" `"$Pfad`" -mem=AES256 -p$($Account.Password) -v2g"
Invoke-Expression $befehl
# Alle Argumente als Elemente eines Arrays
$Arguments = "a","-tzip","`"$Archiv`"","`"$Pfad`"","-mem=AES256","-p$($Account.Password)","-v2g"
sz $Arguments
So funktioniert es nicht:
$Argument="a -tzip `"$Archiv`" `"$Pfad`" -mem=AES256 -p$($Account.Password) -v2g"
sz $Argument

ToDo?

Code Block

Code Block
Und hier noch ein umfassender Beitrag zu dem Thema.

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.
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

Es gibt bei cloudmqtt.com die "süße Katze" für umme. Man bekommt mit der "cute cat"
  • 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

noch ToDo Es gibt eine Version vom November, 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 im Tasmota Wiki.

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