…aber ist das wirklich so?
]]>…aber ist das wirklich so?
Denn tatsächlich hat Tuuvas ein Projekt begonnen, um für so ziemlich jedes Flugzeug im Digital Combat Simulator (DCS) mit einem Gamepad steuern zu können.
Das eigentlich Interessante an seinem Erklärungsvideo ist, dass man mittels Steam einem Gamepad neue Funktionen zuweisen kann, so dass jeder Simulator auf einem PC mit einem Gamepad gesteuert werden könnte.
Seine besonderen Tricks mittels Steam sind:
Tuuvas' Vorschläge für ein Setup sehen grundsätzlich ähnlich aus. Vereinfacht könnte man für jeden Simulator folgende Ideen verwenden.
Gamepad Eingabe | Simulator Ausgabe |
---|---|
Linkes analoges Steuerkreuz | Joystick X/Y |
Rechtes analoges Steuerkreuz | Mauszeiger bewegen |
Schultertasten | Linker / rechter Mausklick |
Schulter-Trigger | Ruder l/r |
Mittlere zwei Buttons | Radbremse l/r |
D-Pad | Trimmung |
Buttons | Schub und weitere Funktionen |
Durch die Verwendung der Maus ist in Simulatoren mit klickbarem Cockpit so ziemlich jede andere Funktion bedienbar, in dem man mit dem Mauszeiger einfach die Knöpfe im Cockpit anklickt.
Kein Joystick, aber ein Gamepad daheim? Steam erlaubt es euch, euer Gamepad in ein HOTAS zu verwandeln, mit dem ihr ganz passabel Flugsimulationen steuern könnt.
BTW: Die Idee mit dem HOTAS im Gamepad-Format wurde auch mit dem Yawman Arrow umgesetzt – kostet dann aber auch wiederum deutlich mehr als ein reguläres Gamepad.
]]>Marcel Reifs Rede bei der Gedenkstunde für die Opfer des Nationalsozialismus im Bundestag am 31.01.2024.
]]>Marcel Reifs Rede bei der Gedenkstunde für die Opfer des Nationalsozialismus im Bundestag am 31.01.2024.
]]>As of today, its successor the Lockheed Martin SR-72 „Son of Blackbird“ has not yet taken flight – even though in cinemas and the Microsoft Flight Simulator the Lockheed Martin Darkstar has surpassed Mach 10 at least once. 😉
]]>As of today, its successor the Lockheed Martin SR-72 „Son of Blackbird“ has not yet taken flight – even though in cinemas and the Microsoft Flight Simulator the Lockheed Martin Darkstar has surpassed Mach 10 at least once. 😉
]]>Aber warum eigentlich ein Framework verwenden? Wozu das Projekt mit Dependencies vollstopfen, wenn ein paar elegante Zeilen Code für unsere Zwecke reichen?
]]>Aber warum eigentlich ein Framework verwenden? Wozu das Projekt mit Dependencies vollstopfen, wenn ein paar elegante Zeilen Code für unsere Zwecke reichen?
In seiner einfachsten Form ist Unit Testing nichts anderes als das Vergleichen von Annahmen mit der Ausgabe von tatsächlichen Code. In JavaScript gibt es dafür eine eingebaute Lösung: console.assert()
.
const starDestroyer = new StarDestroyer();
console.assert(starDestroyer !== null, 'Star destroyer exists');
console.assert(starDestroyer.speed >= 0, 'Star destroyer has speed property with positive Number');
console.assert(starDestroyer.faction === 'imperial', 'Star destroyer is always imperial');
Im Browser wie auch in Node.js erzeugt diese Methode eine Konsolen-Ausgabe, wenn die Annahme nicht zutrifft.
Wenn in ein und derselben Datei mehrere Testfälle geprüft werden sollen, ist eine klares Scoping von Variablen notwendig. So wird sichergestellt, das Ergebnisse aus einem Test nicht irrtümlicherweise im nächsten Test verwendet werden. In JavaScript ist die Erzeugung eines neuen Scopes jederzeit mittels {…}
möglich:
{
const starDestroyer = new StarDestroyer();
console.assert(starDestroyer !== null, 'Star destroyer exists');
console.assert(starDestroyer.speed >= 0, 'Star destroyer has speed property with positive Number');
console.assert(starDestroyer.faction === 'imperial', 'Star destroyer is always imperial');
}
// No starDestroyer here
Mehrere Tests können so in einer Datei abgehandelt werden.
Für automatisiertes Testing ist die Verwendung von console.assert
natürlich zu kurz gesprungen, denn hier kann nur durch Sichtkontrolle überprüft werden, ob alle Tests sauber durchgelaufen sind. Wichtig wäre es also, auf der Kommandozeile einen Exit-Code zu bekommen, der auf einen Fehler hinweist.
Assert
in Node.jsIn Node.js existiert dafür Assert
:
const assert = require('node:assert');
{
const starDestroyer = new StarDestroyer();
assert.ok(starDestroyer !== null, 'Star destroyer exists');
assert.ok(starDestroyer.speed >= 0, 'Star destroyer has speed property with positive Number');
assert.strictEqual(starDestroyer.faction, 'imperial', 'Star destroyer is always imperial');
}
Im Fehlerfall wird assert.AssertionError
geworfen, der auf der CLI einen auch für Automatisierungen wahrnehmbaren Exit-Code erzeugt.
Der Nachteil dieser Lösung ist gut erkennbar: Diese Art von Testing funktioniert nur in Node.js, aber nicht im Browser.
Gott sei Dank ist die Idee mit dem Werfen von Errors aber gar nicht so komplex. Und damit können wir uns eine kleine Funktion konstruieren, mit der wir ganz grundsätzliches Testing durchführen können:
const assertOk = (assertion, message = 'Assertion') => {
console.log((assertion ? "✅" : "💥") + " " + message);
if (!assertion) {
throw new Error(`"${message}" failed`);
}
};
Damit schlagen wir mehrere Fliegen mit einer Klappe:
Etwas aussagekräftiger könnte die Funktion sogar so aussehen:
const assertStrictEqual = (a, b = true, message = '') => {
message === '' || (message += " | ");
message += message = `'${a}' equals '${b}'`;
console.log((a === b ? "✅" : "💥") + " " + message);
if (a !== b) {
throw new Error(`"${message}" failed`);
}
};
Damit können nicht nur direkt Vergleiche durchgeführt werden, sondern auch gleich eine aussagekräftige Nachricht über den tatsächlichen Inhalt des Tests mit ausgegeben werden.
Wenn ihr ein Drop-In-Replacement für assert
von Node.js haben wollt, könnt ihr die beiden Funktionen in statische Methoden verwandeln:
const assert = {
/**
* Check if `assertion` is `true`. Throw Error on error.
* @param {Boolean} assertion
* @param {String} message
*/
ok(assertion, message = 'Assertion') {
console.log((assertion ? "✅" : "💥") + " " + message);
if (!assertion) {
throw new Error(`"${message}" failed`);
}
},
/**
* Check if `actual` strictly equals `b`. Throw Error on error.
* @param {any} actual
* @param {any} expected
* @param {String} message
*/
strictEqual(actual, expected, message = '') {
message === '' || (message += " | ");
message += message = `'${actual}' equals '${expected}'`;
assert.ok(actual == expected, message)
}
};
Oh, und wenn ihr eure Tests noch gruppieren wollt:
console.group('Testing StarDestroyer');
{
const starDestroyer = new StarDestroyer();
assert.ok(starDestroyer !== null, 'Star destroyer exists');
assert.ok(starDestroyer.speed >= 0, 'Star destroyer has speed property with positive Number');
assert.strictEqual(starDestroyer.faction, 'imperial', 'Star destroyer is always imperial');
}
console.groupEnd();
…erzeugt eine schön gruppierte Ausgabe eurer Tests.
Voilà! Eine Beispiel-Ausgabe:
GeoJson.Feature
✅ Type matches | 'Feature' equals 'Feature'
✅ Feature.properties.title | 'Test' equals 'Test'
✅ Feature.geometry.type | 'Point' equals 'Point'
✅ No id
GeoJson.FeatureCollection
✅ 'FeatureCollection' equals 'FeatureCollection'
✅ Bounding Box West | '6.5664576' equals '6.5664576'
✅ Bounding Box South | '51.04571' equals '51.04571'
✅ Bounding Box East | '1.53946' equals '1.53946'
✅ Bounding Box North | '58.109285' equals '58.109285'
✅ FeatureCollection | 'FeatureCollection' equals 'FeatureCollection'
✅ Two Features exist | '2' equals '2'
✅ Feature.type | 'Feature' equals 'Feature'
✅ Feature.properties.title | 'Sailing boat' equals 'Sailing boat'
Natürlich ersetzt diese sehr simple Herangehensweise nicht wirklich echte Unit-Test-Frameworks. So ist zum Beispiel der Vergleich von Objekt-Strukturen nicht möglich, das Testen von Exceptions oder auch die Verwendung von Mocking und ähnlichen Features. Aber zumindest senkt es die Hürde, überhaupt mit Testing zu beginnen. 😉
]]>Jeder Browser strahlt seine eigene Browser-Kennung ab: Den User Agent String. Er verrät nicht nur, welche Browser-Software in welcher Version im Einsatz ist, sondern auch meist das Betriebssystem.
Für einen Tolino Shine sieht das wie folgt aus:
Mozilla/5.0 (Linux; Android 8.1.0; de-; tolino shine 4/16.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Verions/4.0 Chrome/61.0.0.0 Mobile Mobile Safari/537.36
Damit lüften sich mehrere Geheimnisse: Nicht nur ist auf dem Tolino ein Android 8 als Betriebssystem im Einsatz, sondern der Browser ist auch ein veralteter Google Chrome 61. (Zum Zeitpunkt der Untersuchung war gerade der Google Chrome 121 aktuell.) Damit beherrscht er leider nicht alle modernen Tricks von CSS und ECMAScript, ist aber für Brot-und-Butter-Websites immer noch eine sehr gute Wahl.
Mein Tolino Webbrowser hat eine interessante Eigenheit: Er strahlt an jeden besuchten Web-Server einen eigenen Header ab:
X-REQUESTED-WITH: de.telekom.epub
Tatsächlich gibt es möglicherweise historische Gründe für diesen Header, uns hilft dies aber sonst nicht weiter. Viel spannender ist für uns…
Die physische Bildschirmgröße des Tolino Shine 4 ist 1072×1448 Pixel. Im Web wird dabei ein Device Pixel Ratio von 1.875 angenommen.
Nun haben wir auf einem eBook-Reader zwei Herausforderungen, die bei der Gestaltung von Websites berücksichtigt werden müssen:
Sinnvoll wäre also eine Reaktion des Webbrowsers auf folgende Media Queries:
Und hier kommt der etwas traurige Part: In Bezug auf Media Queries gibt es leider keine Anpassungen des Google Chrome auf dem Tolino gegenüber einem normalen Google Chrome auf einem Android-Smartphone oder -Tablet:
Media Query | Value |
---|---|
Media type | screen |
Monochrome | false |
Color | true |
Width | 572px |
Height | 773px |
Resolution (dpi) | 180 |
Colors | 8 |
Orientation | portrait |
Script | false |
Hover | false |
Pointer | coarse |
Prefers reduced motion | false |
Kurz gesprochen gibt es also keine Media Query, mit der das CSS einer Website für den Webbrowser des Tolino zugeschnitten werden kann.
Meine Lösung für dieses Dilemma ist wenig schön, aber besser als nichts:
(function () {
if (window.navigator.userAgent.match(/(Tolino|Kindle)/i)) {
document.body.classList.add("is-ebook-reader");
}
})();
Auf jeder neuen Seite wird mit einer kleinen Zeile JavaScript kurz überprüft, ob es im User Agent String einen Hinweis darauf gibt, dass es sich bei dem besuchenden Browser um einen eBook-Reader handelt. Wenn ja, wird der Seite eine CSS-Klasse is-ebook-reader
hinzugefügt.
Zum Spaß habe ich das hier in diesen Blog eingebaut, und einfach mittels CSS Custom Properties eine kleine Fallunterscheidung in mein CSS gebaut. Eine ganz einfache Lösung könnte aber wie folgt aussehen:
:root {
--color-background: #eee;
--color-text: #112;
--color-link: orange;
--text-decoration-link: none
}
.is-ebook-reader {
--color-background: white;
--color-text: black;
--color-link: black;
--text-decoration-link: underline dotted;
}
a {
color: var(--color-link);
text-decoration: var(--text-decoration-link);
}
]]>Manche Künstler trifft man mehrfach im Leben. Erik Wernquist ist so ein Künstler. „One revolution per minute“ zeigt uns das Leben an Bord eines Kreuzfahrtraumschiffs…
]]>Manche Künstler trifft man mehrfach im Leben. Erik Wernquist ist so ein Künstler. „One revolution per minute“ zeigt uns das Leben an Bord eines Kreuzfahrtraumschiffs…
]]>Die Portfolio-Seite von Ivan Tantsiura ist eine wunderbare Fundgrube. Tantsiura arbeitet als Principal Concept Artist bei Crytek, und hat Unmengen an futuristischen Designs geschaffen – unter anderem das RIDON Hoverbike.
Verglichen mit dem Speederbike aus Star Wars sieht man beim RIDON Hoverbike, warum das Hoverbike schwebt – und auch, dass diese Art von Schweben schon etwas sehr dynamisches hat. Der Mechanismus (wie auch die Welt des Hoverbikes) erinnern an Simon Stålenhag und seine schwebenden Schiffe.
Man bekommt direkt Lust, selber eine kleine Spritztour mit dem RIDON Hoverbike zu machen. Wo bleibt also der Arcade-Automat beziehungsweise das VR-Spiel zum Thema?
]]>Die Portfolio-Seite von Ivan Tantsiura ist eine wunderbare Fundgrube. Tantsiura arbeitet als Principal Concept Artist bei Crytek, und hat Unmengen an futuristischen Designs geschaffen – unter anderem das RIDON Hoverbike.
Verglichen mit dem Speederbike aus Star Wars sieht man beim RIDON Hoverbike, warum das Hoverbike schwebt – und auch, dass diese Art von Schweben schon etwas sehr dynamisches hat. Der Mechanismus (wie auch die Welt des Hoverbikes) erinnern an Simon Stålenhag und seine schwebenden Schiffe.
Man bekommt direkt Lust, selber eine kleine Spritztour mit dem RIDON Hoverbike zu machen. Wo bleibt also der Arcade-Automat beziehungsweise das VR-Spiel zum Thema?
]]>Dummerweise lässt sich der Sommermodus an den Thermostaten vom Home Assistant aus nicht einfach auslösen. Und selbst die im Home Assistant vorhandene Möglichkeit, den Thermostat einfach auszuschalten, funktioniert nicht wirklich gut – der nächste Schaltbefehl an den Thermostat hebt die Abschaltung wieder auf. Außerdem kann der Sommermodus deutlich mehr als nur die Heizung abzuschalten – unter anderem kümmert er sich um das gelegentliche Bewegen der Ventile, um sie vor dem Steckenbleiben zu bewahren.
…ist zweiteilig: In der FRITZ!Box legen wir mittels Vorlagen das ein- und ausschalten des Sommermodus' an – und im Home Assistant lösen wir dann die Vorlagen aus.
Den Druck auf einen dieser Knöpfe könnt ihr im Hone Assistant abfangen und zusätzliche Aktionen auslösen.
Dazu müsst ihr eine Automatisierung bauen, die beim Druck auf die Knöpfe neben den Vorlagen in der FRITZ!Box noch zusätzliche Aktionen auslösen:
alias: "Heizungen: Sommermodus an"
description: ""
trigger:
- platform: device
device_id: 5f6c4925f76d4ef27407160a968bb7fd
domain: button
entity_id: button.heizungen_sommermodus_an
type: pressed
condition: []
action:
- service: notify.notify
data:
title: "Sommermodus an"
message: >-
Alle Heizungen sind nun abgeschaltet, willkommen im Sommermodus.
enabled: true
mode: single
Wir haben aber ja die ganze Arbeit nicht auf uns genommen, um im Home Assistant selber auf Knöpfe drücken zu müssen. Tatsächlich können wir Automatisierungen des Home Assistants für uns auf den Knopf drücken lassen.
Dabei kann in jeder Automatisierung als Aktion der Druck auf die Knöpfe ausgelöst werden:
action:
- service: button.press
data: {}
target:
entity_id: button.sommermodus_an
enabled: true
Somit könnt ihr euch nun beliebige Auslöser im Home Assistant konfigurieren, die eure Heizung in den Sommermodus schicken.
Die selbe Methode lässt sich neben dem Sommermodus auch für den Urlaubsmodus und eigentlich jede andere Vorlage in der FRITZ!Box anwenden. Dafür müsst ihr nur jeweils in der FRITZ!Box das gewünschte Verhalten als Vorlage definieren, um danach den zugehörigen Button im Home Assistant drücken zu können – beziehungsweise durch eine Automatisierungen auslösen zu können.
Theoretisch könnt ihr darüber auch abweichende Zeitpläne oder geänderte Spar-/Komforttemperaturen oder zeitlich begrenzte EInstellungen wie Boost oder Fenster-Modus aktivieren.
]]>Was benötigen wir, um das Spiel wieder auferstehen zu lassen?
So verrät uns der Wikipedia-Eintrag zu „War at Sea“ nicht nur, dass zuletzt um die 280 verschiedene Einheiten für das Spiel verfügbar waren, sondern verlinkt auch die vom Hersteller frei zugänglich gemachten Spielregeln von „War at Sea“ – wenn auch diese inzwischen nur noch über web.archive.org erreichbar sind. Die Spielregeln erklären nicht nur das Spiel, sondern zeigen auch, welche weiteren Teile ihr für das Spiel benötigt.
Wenn ihr die dazu die „War at Sea“-Tabletop-Spielregeln benutzt, braucht ihr auch kein Spielfeld, sondern könnt das Spiel auf einer beliebigen Oberfläche (wie zum Beispiel dem Wohnzimmerfußboden) spielen.
Außerdem gibt es im Internet frei verfügbare Datenbanken über alle in „War at Sea“ verfügbaren Einheiten und sogar die Designs der Sammelkarten. Der Charme der ursprünglichen Sammelkarten kommt damit zwar nicht auf, aber dafür hat man Zugriff auf alle Einheiten.
Jetzt fehlen nur noch die Spielfiguren. Eigentlich könnte man hier ja alles nehmen, was in irgendeiner Form ein Schiff (beziehungsweise fliegende Einheiten) darstellt. Mit passenden Schiffsminiaturen kommt man aber deutlich näher an das ursprüngliche Spielgefühl.
Dazu muss man nur wissen, wie groß die Spielfiguren damals waren:
Die 266 Meter lange USS Essex misst 148 mm. […] Der Maßstab scheint mir 1:1800 zu sein.
Da dieser Maßstab nicht unbedingt üblich ist, könnte man sich hier mit 3d-gedruckten Schiffen behelfen.
Bei Thingiverse gibt es ausgezeichnete Schiffs-Miniaturen für „War at Sea“, die ihr nur noch mit einem 3d-Drucker drucken lassen müsst – und schon könnt ihr euer eigenes Unternehmen Rheinübung ausprobieren.
]]>Alle Branches sind gepusht,
ich meld' mich ab.
Alle Branches sind gepusht,
ich meld' mich ab.