Gemstone/S, PDFtalk – insert an image

I am using Gemstone/S for various tasks and together with Christian Haider we looked for an example code to insert a jpg-image into a PDF page. This post is a reminder of this message.

The following code reads a jpg file from disc and puts the image on a pdf page.

 | jpegFile jpgData aStream soi   abort P Y X Nf  vm anImageXObject pdfDocument page |

"We get the binary data of the jpg image"
jpegFile := GsFile openReadOnServer: '/home/mf/demo-grafik.jpg'.
jpgData := jpegFile contents.
jpegFile close.

"the first step is to analyse the content of the jpg ... this is example
code !"
aStream := ByteReadStream  bigEndianOn: jpgData asByteArray.

soi := aStream next: 2.
(soi first = 255 
	and: [ soi last = 216 ]) ifFalse:[ self halt: 'not a SOI structure' ].

abort := false.
[ abort ] whileFalse:[ 
	| headerLength frameHeader | 

	"get header"
	frameHeader := aStream next: 2.
	(frameHeader first = 255 
		and: [ frameHeader last >= 192 
			and: [ frameHeader last <= 255 ]]) ifFalse:[ self halt ].
	
	headerLength := aStream nextUnsignedShort.

	frameHeader last = 192 
			ifTrue:[
				"the is the header we want to get"
				abort := true.

			]
			ifFalse:[
				"not a header we are interested in"
				aStream next: headerLength - 2
			]
].

"the stream points to the right header ... now we retrieve the base meta data"
P := aStream next.
Y := aStream nextUnsignedShort.
X :=  aStream nextUnsignedShort.
Nf :=  aStream next.

"now to the pdf handling"

"make a dictionary, Valuemap is defined in PDFtalk"
vm := Valuemap new.
vm
	at: #Width put: X ;
	at: #Height put: Y ;
	"can be computed by examine the value of Nf - we assume always the same here"
	at: #ColorSpace put: #DeviceRGB;
	at: #BitsPerComponent put: P ;
	at: #Type put: #XObject ;
	at: #Subtype put: #Image.

"Now create a pdf object"
anImageXObject := 
	(PDF classAt: #ImageXObject)
		on: vm.

anImageXObject 
	contents: jpgData ;
	at: #Filter put: #DCTDecode.


"We create a landscape pdf page"
page := PDF Page 
				newInBounds: (0 @ 0 corner: 842 @ 595) 
				colorspace: (PDF classAt: #DeviceRGB) new 
				render: [:renderer |

	"pt unit = 1/72 inch"
	renderer isolatedDo: [
		renderer 
			paintImage: anImageXObject 
			caption: 'Hochrechnung' 
			in: (34@34 extent:  192 @ 108).
	].
].
page saveAndShowAs: 'test.pdf'

This code can be seen as a base for putting jpg graphics into a PDF report.

Thanks to Christian for the small whereby conference to get this code to evolve.

Advertisement
Posted in Smalltalk | Tagged , , , , | Leave a comment

Wärmepumpe und Förderung

So, heute ist der Bescheid über die Förderung gekommen und auch der genaue Förderbetrag wurde genannt. Dauer der Bearbeitung ca. 4 Monate.

Was einem dabei auffällt – solche Förderung verfolgt immer zwei Punkte:

  • Förderung des eigentlichen Thema
  • Förderung des Handwerks

Alles, was selber gekauft wurde (und mit Beleg nachgewiesen wurde: Smart-Regler für die diversen Heizkörper, Netzwerkkabel, Stromzähler für die Wärmepumpe für S0-Signalerzeugung), fiel aus der Förderung raus: Eigenleistung nennt sich das. Die Leistung des Handwerkers – meinen eigenen Stromzähler einzubauen – wird allerdings gefördert.

Baut man also selber das Fundament, dann bekommt man die Rohstoffe nicht gefördert.

Wenn man bedenkt, dass der erste Handwerker die Heizkörper mit 200% Gewinn an mich verkaufen wollte und ich anbot, die Heizkörper selber zu kaufen – die völlig überteuerten Heizkörper werden mit 30% subventioniert – bei Eigenkauf, wird nichts unterstützt.

Egal, beim nächsten Mal also alles auf die Handwerkerrechnung.

Posted in Smalltalk | Tagged , | Leave a comment

StateManagement, ExtJS, Widgets, Gemstone/S und PUM

Sencha ExtJS benutzt zur Speicherung der State-Information der UI-Komponenten die Instanz Ext.state.Manager. Der bekommt eine Instanz zugewiesen, die drei verschiedene Operationen implementieren muß.

Hier eine Beispielimplementation des StateProvider. Die Daten werden in Objekten – verwaltet durch ein Store – mit den Attributen key und value gespeichert:

/**
 * Created by mf on 16.12.22.
 */
Ext.define('Ctm.RemoteStateProvider', {
    extend: 'Ext.state.Provider',
    // Der Store muss auf autoSync = false und autoLoad = false definiert sein
    config: {
        store: null
    },
    constructor: function (config) {
        var me = this;
        Ext.apply(me, config);
        me.state = {};
        me.mixins.observable.constructor.call(me);
    },
    set: function (name, value) {
        let me = this;
        let pos, row;
        if ((pos = me.store.find('key', name)) > -1) {
            row = me.store.getAt(pos);
            row.set('value', me.encodeValue(value));
        }
        else {
            me.store.add({
                key: name,
                value: me.encodeValue(value)
            });
        }
        if(me.store.isSyncing) return;
        me.store.sync();
        me.fireEvent('statechange', me, name, value);
    },
    get: function(name, defaultValue) {
        let me = this;
        let pos, row;
        if ((pos = me.store.find('key', name)) > -1) {
            row = me.store.getAt(pos);
            return me.decodeValue(row.get('value'));
        }
        else {
            return defaultValue;
        }
    },
    clear: function(name) {
        let me = this;
        let pos;
        if ((pos = this.store.find('name', name)) > -1) {
            me.store.removeAt(pos);
            me.store.sync();
            me.fireEvent('statechange', this, name, null);
        }
    }
});

Dazu gehört ein Store, der die Daten in der Gemstone/S speichern soll. Er muß alle 4 CRUD Operationen implementieren. In PUM kann man den Store definieren auf ein DomainObjekt mit den Attributen key und value, die beide eine Zeichenkette speichern sollten. Eine gute Position ist eine 1:n Assoziation von einer Klasse, die den ApplicationUser darstellt.

Diesen Store sollte man in der JS-Anwendung früh definieren und ein load() ausführen. Eine statische Definition in einem ViewModel im ersten Fenster ist zu spät. Der Store muß nach einem erfolgreichem Login erzeugt werden, damit eine korrekte URL durch das Framework erzeugt wird.

uiStateStore = new ctm.store.CTMAPIUIStateValueList();
uiStateStore.load(
    function(records, operation, success) {
        Ext.state.Manager.setProvider(Ext.create('Ctm.RemoteStateProvider',{
            store: uiStateStore
        }));

    // Nun geht es mit der Anwendung los ...
        me.redirectTo("....")
});

Um z.B. ein GRID mit der state-Verwaltung zu beauftragen, kann man ein GRID wie folgt definieren (Attribute stateful und stateId definieren):

{
    xtype: 'gridpanel',
    stateful: true,
    stateId: 'sessiongrid'
}

Auf der Gemstone-Seite muß man einen Store definieren, eine Domain- und eine API-Klasse und die notwendigen Calls für alle Aktionen. Es folgen Parameter und Rückgabe-Hilfsklassen. Hier eine Beispielimplementierung der vier Methoden unter Gemstone/S:

Read:

apiUiCTMAPIUIStateValueListRead: aCTMAPIPRUIStateValueListStore 
options: aMSKRestCallOptions

 | aCTMAppUser |

 aCTMAppUser :=  aMSKRestCallOptions session getAppUser.

 ^(self restAnswerClass 
      okRequestFor: aMSKRestCallOptions restCallInstance                           
      result: (CTMAPIUIStateValueListStoreRL 
                    withDomainList: aCTMAppUser getUiStateValues 
                    apiClass: CTMAPIUIStateValue  
                    total: aCTMAppUser getUiStateValues ))

     successWithoutCommitCall;
     yourself

Create:

apiUiCTMAPIUIStateValueListCreate: aCTMAPIUIStateValueListStorePL 
options: aMSKRestCallOptions

 | list aCTMAppUser |

 aCTMAppUser :=  aMSKRestCallOptions session getAppUser.

 list := aCTMAPIUIStateValueListStorePL getItems collect: [ 
    :eachCTMAPIUIStateValue |
    | newCTMUIStateValue newCTMAPIUIStateValue tmpString |

    newCTMUIStateValue := CTMUIStateValue newInitialized.

    (tmpString := eachCTMAPIUIStateValue copyTo: newCTMUIStateValue) isNil
       ifFalse:[ 
          CTMEnumErrorDefinitionLocaleErrorDefinition 
             errCodeGeneralErrorThrowSignal: aMSKRestCallOptions 
             with1Args:(Array with: tmpString asString)  ] .

    aCTMAppUser addUiStateValues: newCTMUIStateValue.

    (CTMAPIUIStateValue copyFrom: newCTMUIStateValue)
        setSyscid: eachCTMAPIUIStateValue getSyscid;
        yourself

 ].                

 ^(self restAnswerClass 
     okRequestFor: aMSKRestCallOptions restCallInstance 
    result: (CTMAPIUIStateValueListStoreRL withAPIList: list total: list))
     successWithCommitCall;
     yourself

Update:

apiUiCTMAPIUIStateValueListUpdate: aCTMAPIUIStateValueListStorePL 
options: aMSKRestCallOptions
  | aCTMAppUser list |
  aCTMAppUser :=  aMSKRestCallOptions session getAppUser.
  list := aCTMAPIUIStateValueListStorePL getItems collect: [ 
   :eachCTMAPIUIStateValue | 
      | aCTMUIStateValue tmpString |
    (aCTMUIStateValue := aCTMAppUser getUiStateValues 
        detect: [ :each | each getGop = eachCTMAPIUIStateValue getGop] 
        ifNone:[ nil ]) isNil
       ifTrue:[ 
           CTMEnumErrorDefinitionLocaleErrorDefinition 
              errCodeGeneralErrorThrowSignal: aMSKRestCallOptions  
              with1Args: (Array with: 'UIState Item not found') ].

    (tmpString := eachCTMAPIUIStateValue copyTo: aCTMUIStateValue) isNil
      ifFalse:[ 
         CTMEnumErrorDefinitionLocaleErrorDefinition 
            errCodeGeneralErrorThrowSignal: aMSKRestCallOptions  
            with1Args: (Array with: 'UIState not copyable')  ] .

      aCTMUIStateValue
 ].

 ^(self restAnswerClass 
      okRequestFor: aMSKRestCallOptions restCallInstance
      result: (
          CTMAPIUIStateValueListStoreRL 
              withDomainList: list
              apiClass: CTMAPIUIStateValue  
               total: list))
     successWithCommitCall;
     yourself

Delete:

apiUiCTMAPIUIStateValueListDestroy: aCTMAPIUIStateValueListStorePL 
options: aMSKRestCallOptions

 | aCTMAppUser  |

 aCTMAppUser := aMSKRestCallOptions session getAppUser.

 aCTMAPIUIStateValueListStorePL getItems do: [ 
     :eachCTMAPIUIStateValue | 
     | aCTMUIStateValue  |

     (aCTMUIStateValue := aCTMAppUser getUiStateValues  
      detect: [ :each | each getGop = eachCTMAPIUIStateValue getGop] 
      ifNone:[ nil ]) isNil
   ifTrue:[ 
      CTMEnumErrorDefinitionLocaleErrorDefinition 
        errCodeGeneralErrorThrowSignal: aMSKRestCallOptions  
        with1Args: (Array with: 'UIState Item not found') ]
   ifFalse:[ 
       aCTMAppUser removeUiStateValues: aCTMUIStateValue ].
 ].

 ^(self restAnswerClass 
         okRequestFor: aMSKRestCallOptions restCallInstance 
      result: (CTMAPIGeneralResult newInitialized setSuccess: true ; 
          yourself))
    successWithCommitCall;
    yourself

Posted in Smalltalk | Tagged , , , | Leave a comment

Wärmepumpe, die Wochenshow

Dieses Posting dient der Zusammenfassung und Dokumentation – auch weil ich jetzt weiß, wie man in openHab mehrere Informationen gleichzeitig darstellt.

Zuerst der Abgleich der Temperaturen – via Internet für meine Gemeinde gemeldet/hochgerechnet und die Messung durch die WP selber:

Das war eine kalte Woche – nahezu 8 Tage mit Dauerfrost (wenn man den 16.ten rausnimmt) – das könnte der “Putin”sche Winter werden.

Kommen wir zu der zentralen Grafik:

  • Außentemperatur
  • Leistung elektrisch gezogen
  • E-Heizung
  • Wohnzimmertemperatur

Die Wohnzimmertemperatur (blau) dümpelt so um 20 Grad rum, die kleinen Peaks sind direkter Sonnenschein durch die Glasfront.

Die E-Heizung (gelb) zeigt sich ab -5 Grad im StandBy und schaltet sich dazu (nun weiß ich auch warum – Bivalenzpunkt steht in der Konfiguration mit -5 Grad).

Der Stromverbrauch liegt am 17.ten um und bei 85 kW pro Tag – neue Höchstmarke.

Und nun noch die verschiedenen Temperaturen im System selber:

  • Außentemperatur
  • E.Heizung
  • Kessel
  • Rücklauf
  • Sammler
  • Vorlauf-Mischer

Und nun noch ein Blick auf die Warmwasseraufbereitung:

Posted in Smalltalk | Tagged , , | Leave a comment

Wärmepumpe feiert Stromverbrauch

Ok, die letzten Nächte waren mit bis zu -8 Grad richtig frostig – der Stromverbrauch hat absolute Höchststände erreicht. Hoffe mal, daß das Wetter wieder besser wird.

Heute hat es hier – sehr selten an der Westküste – ordentlich geschneit. Gut und gerne 10cm sind hinzugekommen und der Schnee hat sich auf die Lamellen gelegt. Bevor die schmelzen und festfrieren, habe ich die Lamellen gereinigt.

Am unteren Bereich der Lamellen bildet sich langsam eine feste Eisschicht, die nicht mehr abgetaut wird. Ich muß beim nächsten Tauwetter darauf achten, daß das alles verschwindet …

Wir sind jetzt bei 0 Grad und wieder bei einer Effizienzzahl von 4.0, gestern gab es eine Effizienzzahl von 2.2 bei -8 Grad.

Morgen nacht erwarten wir -12 Grad … das ist ja ein schöner Test. Danach geht es mit den Temeraturen wieder bergauf.

Posted in Smalltalk | Tagged , , | Leave a comment

Eine Wärmepumpe muss sich ins Zeug legen ..

Lange nichts gehört – aber heute sollte man mal wieder ein Posting hinlegen. Wir haben winterliche Temperaturen – und zwar durchaus winterliche Temperaturen mit 7-8 Grad Frost.

Und hier die Stromaufnahme dazu (Aua) …

Was sieht man hier ? Der E-Stab wird zugeschaltet – nicht zu 100%, aber anteilig.

Die Effizienzzahl lag in den letzten Tagen bei 2.8 bis 3, weit entfernt von den 4 bis 5 im Herbst.

Also an den Tagen wie heute gehen gut und gerne 50kw durch den Stromzähler, was €10 pro Tag ausmacht (eon grundversorgung heizstrom).

Das tut zwar weh, aber ich habe gerade meine letzte (in wahrsten Sinne) Gas-Abrechnung bekommen – dort zeigte sich, daß bis zu 60% des gesamten Jahresverbrauches zwischen dem 20.12 und dem 28.02 – also in nur 3 Monaten – verbraucht wurden.

Inzwischen kann ich sogar feststellen, daß die Heizkörper im Hause (in den oberen Etagen) angesprungen sind und fühlbar warm wurden. Ich vermute, daß man dies an den Kesseltemperaturen ablesen kann:

Posted in Smalltalk | Tagged , , | Leave a comment

Eine Wärmepumpe und der Winter … (14)

Nun ackert die WP und die Geldzahlungen legen los.

18.11.2022

Wenn ich mal den 18.11. betrachte mit den Temperaturen:

dann sieht man einen Tag mit 0 – 3 Grad. Wobei meine Internetauskunft mit um Mitternacht schon -2 Grad meldete und nicht die 0 Grad, wie die WP selber.

Die Arbeit der WP ist geprägt vom Abtauen. Hier die Stromaufnahme des Gerätes:

Um 13:30 wieder die gewohnte Warmwasserzubereitung. Der Verbrauch an diesem Tag lag bei 40 KW (therm. Energie: 133 KW) bei einer TAZ VT von 3.3. Das sind also beim aktuellen Stromtarif knapp 8€.

Sound ?

Ja, man hört sie. Ein intensives Rauschen gibt sie von sich – ging im Wind von gestern unter – aber nun ist Windstille angesagt und die WP ist eindeutig zu vernehmen.

19.11.2022

Und der nächste Tag wird noch kälter – im Durchschnitt. Verbraucht werden 44 KW bei einer th. Energiemenge von 135KW und einen TAV VT von 3.1.

Temperatur

Leistungsaufnahme (KW/h)

23.11.2022

So, die Kälte läßt nach. 24 el.KW und 93 th.KW bei 3.9:

Hier die Temperaturen vom 23.11 bis in den 24.11

und hier die Leistungsaufnahme im gleuchen Zeitrau, die zeigt, wie sich die WP langsam wieder beruhigt:

Die WP arbeitet wieder auf der geringsten Modulationsstufe.

Also Fazit für mich: bei einer Temperatur von 4 bis 5 Grad beginnt die “Gefahr” der Vereisung der WP-Lamellen. Dann sinkt die TAV VT auf bis zu 3 ab.

Die WP hat sich dann alle 1.5 bis 2.5 Stunden enteist, in den oberen Grafiken kann man bis zu 10 Peeks ablesen.

Blätter

Ach ja, diese Blätter von den Bäumen – bei uns sind viele Blätter, denn im Gegensatz zu unseren nachbarn, haben wir alle Bäume behalten und versuchen nichts zu fällen.

Man soll ja den Luftaustritt der WP nicht in die Hauptwindrichtung ausrichten, also hatten wir die WP gedreht (auch damit der Austritt nicht direkt zum Nachbarn zeigt)- der Austritt zeigt nach Norden – aber nun pustet der Wind hinten rein … und bringt die Blätter mit. Das Schutzgitter soll die Lamellen der WP eher vor groben Sachen schützen – sicherlich nicht vor Blättern. Also müssen wir die Lamellen bei Laubfall immer wieder von den Blättern reinigen.

Posted in Smalltalk | Tagged | Leave a comment

Eine Wärmepumpe kommt ins Haus … (13)

Der Winter hat meine Wärmepumpe “Wolfi” erreicht. Anders kann man es nicht sagen. Seit gestern also feuchtes, kaltes Wetter mit regelmäßigem Niederschlag.

Das Heizprogramm ist nun für den gesamten Tag aktiviert und “Wolfi” muß richtig arbeiten …

Zuerst einmal muß man sagen, daß die Temperaturen noch oberhalb von 0 Grad liegen:

Und es lag ein regelmäßiger Wind aus östlicher Richtung vor und ab Nachmittag sehr feucht.

Das führte nun dazu, daß die Heizung nun regelmäßig in der Nacht enteisen muß:

Ich gehe mal davon aus, daß die Peaks in der Nacht Enteisungsvorgänge sind (einen habe ich mir sogar angeschaut, um das mal gesehen zu haben). Und der Stromverbrauch ist hochgegangen. Gestern wurden 31 kw (127 thermische kw erzeugt) verbraucht bei einer TAZ VT von 4.1.

Die folgende Grafik zeigt den Strombedarf über den gesamten Tag ( 17 .. 18) und die Grafik zeigt, daß die WP den gesamten Tag aktiv war:

Tja, das kann ja im Winter noch nett werden:

Posted in Smalltalk | Tagged , , , | Leave a comment

TOTP-Support under Gemstone/S

We had yesterday a small video session using whereby and some Smalltalkers diskussed the topic “totp” as second authentification method after an successful login (we were VASmalltalk, VisualWorks and Gemstone related).

To get all work done under Gemstone you need the MSKLibQREncode, the Multibase-Code and one class from the MSK-ModelBaseRuntime package: MSKTOTPSupport.

The MSKTOTPSupport class has three class methods with all stuff needed. Perhaps some methods are defined elsewhere, but you should at least get the ideaa …

totpCurrentValueForSecret: keyArray forTime: aDateAndTime digits: numberOfDigits period: seconds algorithm: aString
” Returns a new value or nil in error case

totpCurrentValueForSecret: keyArray 
forTime: aDateAndTime 
digits: numberOfDigits 
period: seconds 
algorithm: aString
	"^Integer | nil Returns a new value or nil in error case

			MSKTOTPSupport 
               totpCurrentValueForSecret:  'joachim' asByteArray 
               forTime: DateAndTime now 
               digits: 6  
               period: 30 
               algorithm: 'sha256'
	"
	| hmac key  offset binaryValue currentTimeIntervalStep byteArray |

	currentTimeIntervalStep := ((aDateAndTime asSeconds floor - 
                                                  (DateAndTimeANSI  posixSeconds: 0 offset: (Duration zero) ) asSeconds floor) / seconds) floor .

	byteArray := currentTimeIntervalStep asByteArrayOfSize: 8 .

	aString = 'md5' ifTrue:[ hmac := byteArray asMd5HmacWithKey: keyArray ].
	aString = 'sha1' ifTrue:[ hmac := byteArray asSha1HmacWithKey: keyArray  ].
	aString = 'sha256' ifTrue:[ hmac := byteArray asSha256HmacWithKey: keyArray  ].
	aString = 'sha512' ifTrue:[ hmac := byteArray asSha512HmacWithKey: keyArray  ].

	hmac isNil ifTrue:[ ^nil ].

	hmac := hmac asByteArray.

	"letztes byte nehmen"
	offset := ((hmac at: hmac size) asInteger bitAnd:16r0f) +1.

	binaryValue := (((hmac at: offset) bitAnd: 16r7f) bitShift: 24)
							+ (((hmac at: offset + 1) bitAnd: 16rff) bitShift: 16)
							+ (((hmac at: offset + 2) bitAnd: 16rff) bitShift: 8)
							+ (((hmac at: offset + 3) bitAnd: 16rff) ). 

	^binaryValue \\ (10 raisedTo: numberOfDigits)

totpQRStringForSecret: keyByteArray 
label: identification 
period: seconds 
issuer: issuerString 
digits: digits 
algorithm: algName 
icon: iconURL 
color: colorValueStringRRGGBB
"
MSKTOTPSupport 
  totpQRStringForSecret: 'joachim' asByteArray 
  label: 'cati' 
  period: 30 
  issuer: 'GESSCati' 
  digits: nil 
  algorithm: 'sha256' 
  icon: nil

        https://github.com/npmccallum/freeotp-android/blob/master/URI.md
"

| writeStream |

writeStream := WriteStream on: String new.
writeStream
    nextPutAll: 'otpauth://totp/' ;
    "URI-encoded "
    nextPutAll: identification encodeForHTTP ;
    nextPutAll: '?secret=' ;
    nextPutAll: (MultibaseBase32upper new encode: keyByteArray).

(digits notNil and:[ digits ~= 6 ])
    ifTrue:[ writeStream nextPutAll: '&digits=' ; nextPutAll: digits asString  ].


(seconds notNil and:[ seconds ~= 30 ])
    ifTrue:[ writeStream nextPutAll: '&period=' ; nextPutAll: seconds asString  ].

(issuerString pumIsStringAndNotEmpty)
    ifTrue:[ writeStream nextPutAll: '&issuer=' ; nextPutAll: issuerString encodeForHTTP ].

(algName pumIsStringAndNotEmpty and:[ algName ~= 'sha1' ])
    ifTrue:[ writeStream nextPutAll: '&algorithm=' ; nextPutAll: algName ].

(colorValueStringRRGGBB pumIsStringAndNotEmpty)
    ifTrue:[ writeStream nextPutAll: '&color=' ; nextPutAll: colorValueStringRRGGBB ].

(iconURL pumIsStringAndNotEmpty)
    ifTrue:[ writeStream nextPutAll: '&image=' ; nextPutAll: iconURL ].

^writeStream contents

totpSecretAsBased32: keyByteArray
"
MSKTOTPSupport totpSecureKeyBased32: 'joachim' asByteArray
"
^MultibaseBase32upper new encode: keyByteArray

So by writing the following code, we could generate a png showing a QR code, which FreeOTP could use:

(MSKLibQRToolOptions
   newPNGFor: (MSKTOTPSupport
                  totpQRStringForSecret: 'joachim' asByteArray
                  label: 'GessCATI:M.Feldtmann'
                  period: 30
                  issuer: 'Anonymous'
                  digits: 6
                  algorithm: 'sha1'
                  icon: 'https://feldtmann.ddns.net/test.png'
                  color: '808080')
   path: '/var/www/html/Sebastian.png')
      moduleSize: 15 ;
      foreground: Color black ;
      background: Color white ;
      qrToolCreateQRCode

The system uses the local qrencode tool available under Linux and we got the following QR png file – and you may try your FreeOTP with that result.

Some question still remain when other algorithm have to be used, but this will be solved – and how the remaining other algorithm could be inserted.

Working Model – Server Side

Here is a description of a working model (on the server side) and a description of a potential API to manage TOTP systemwide. Sorry, its in German.

Posted in Smalltalk | Tagged , , , | 1 Comment

Tuxedo Pulse 15 Gen2

I bought an new laptop – I mean really a “new” laptop. I decided to buy this time a new laptop with preinstalled, encrypted Linux operating system Tuxedo OS 1 (based on Ubuntu 22.04) and I’ve chosen the Tuxedo Pulse 15 Gen2.

General Description

You may use the link above to get more technical information about this laptop. In this posting I only mention some informations which seem to be perhaps not mentioned somewhere and are important for me.

In the past I have used several other machines – the Thinkpad W530 (the last classical Thinpad machine from Lenovo with good quality):

Then the 17” ZBook17, Gen2 from HP – dated a little bit later than the W530 – but with lots of room inside. But a very heavy laptop (around 3.x kg), coming into age and when working – getting louder and louder now.

Then at the beginning of this year I bought a newer machine – the Huawei Matebook 16 (AMD CPU pretty similar to the Pulse machine) – and used it in a dual boot configuration for Windows and Linux:

The Matebook has much more power than the older machines – and I installed Ubuntu 22.04 LTS on that machine. The keyboard is not that good as on the older machines and has some quality problems. The left shift key is sometimes not working very well. The dual boot options is making problems with new versions of the kernel and VirtualBox under Ubuntu. I’ve to sign the Ubuntu VirtualBox files again and again to be able to start the VirtualBox. No sound under Linux, no background light of the keyboard … and no rj45 tcpip network port …

The machine has only 16 GB RAM and this is a main limiting point under Linux when doing development in the area I am working with. I assumed, that it would be ok, but I hit that limitation several times … and of course the builtin SSD with a size of 512GByte is also limiting. So I added an external hub to get access to external harddisc drives, I added a new power supply (based on USB-C) to feed the hub and the additional drives. That became a cable mess …. and at one working event, I forgot an external SSD where some badly needed files were located on :-(.

So I looked for a new machine and the Matebook will become a Windows only machine now …

So I looked for a development machine with more RAM, more harddisc space and not as heavy as the ZBook17 I mentioned earlier.

The picture below show from top to buttom: Tuxedo, Matebook, Lenovo W530 and ZBook 17

And I wanted to have no problems with Linux and for the first time I decided to build a new, linux only laptop. I ordered it from the Tuxedo web-page and added additional RAM (now 64 GB) and harddisc space (now 4TB).

Ordering and Delivering

The laptop arrived around 10 days after I ordered it and it was well packaged and delivered to me. Actually I was surprised to get it delivered at that day – no information was sent to me, that I should prepare myself for the post man … I got that eMail notification two days later after arrival of the laptop. DHL seems to be very fast this time 🙂

Screen

The screen has a higher resolution than the ZBook or the Matebook – and normally this would be not useful for my eyes – but at 125% with KDE most of the tools can be used quite nice.

The screen is bright enough for me, the quality is good and I like the display.

Connectivity

The system has an additional HDMI port (which I use for a 4K display), the RJ45 network port and two fast USB port (the Matebook has also USB ports, but only slow ones) and one USB-C port. Perhaps useful may also the 3.5 sound port for external boxes.

Keyboard

The keyboard has the quality of the ZBook17, Gen2 keyboard I had used for some time in the past (which had the best keyboard I have ever worked with, even better than the Lenovo W530 I also used) and it is far better than the Matebook16 system. Yes, there is no numpad – but for a 15” laptop this is ok. The keys have the correct sizes for me and typing can be done pretty fast.

Strange for me is, that to press F12 (debugger tool in browsers) I have to press Fn and F12 to get there … normally I simply pressed F12 – but on this machine the laptop goes into the standby mode. I will learn that …

When I touch the keyboard the backlit light of the keyboard is going on and that is working under Linux. I never got this working with the Huawei Matebook 16.

Sound

It is working under Linux, out of the box. The quality is not that good and the speaker are located on the bottom side of the laptop. The Matebook 16 is much better in this area (under Windows), but the Matebook16 system is not yet supported under Ubuntu 22.04.

Fan Noise

The laptop is a pretty silent machine … but if you get an average load of about 6 over some time you will hear the machine. Some java programs will help you to find out … its not getting very much loader if you have a load of 6 or of 10.

The fan manages to cool the system down while the fan is running at around 60% and the system seems to be stable running at 3 Ghz. That are good news.

WebCam

A webcam is build into the system, just above the screen (and not as done at the MateBook in the keyboard ….). It is working, the quality is not so good – but for me totally ok.

Operating System

The OS behaves like an Ubuntu 22.04, but with a much nicer UI. During installation (or better final configuration) I was not able to enable the WLAN support to upgrade the system to the latest system level. After the installation this worked of course, so I made an update after that.

Conclusion

The system is working out of the box, has an up to date operating system, the UI looks pretty nice, is quite powerful with up to 16 threads, can be configured with up to 64 GB of RAM and both NVM SSDs are very fast. The screen has a very good quality … and its weight is around 1.5 kg.

Connections

The pictures show all possible connectors on the left and right of the computer:

So, that’s it for now … back to the computer and having fun.

Posted in Smalltalk | Tagged , , , | Leave a comment