Orient: Life at Sea in the 21st Century
The Titanic could accommodate about 2500 passengers who booked either first, second or third class. Wikipedia tells that
those travelling in first class, most of them the wealthiest passengers on board, included prominent members of the upper class, businessmen, politicians, high-ranking military personnel, industrialists, bankers, entertainers, socialites, and professional athletes. Second-class passengers were predominantly middle-class travellers and included professors, authors, clergymen, and tourists. Third-class or steerage passengers were primarily immigrants moving to the United States and Canada.
Much has changed since then.
Life at Sea in the 21st Century
Purpose and passengers of such mega ships (rather kilo ships :wink:) have changed dramatically since then. The ship is no longer a mean of transport. Passengers from Central Europe fly 5000 km to the Orient for a cruise of 500 km to a nearby harbour and back to the point of departure. Many passengers are rather age group 50+ and have cruised around quite a lot already1. Then, there are few younger families and couples as well. Other single travellers fall rather into the category of widows2.
My ship features 1267 twin cabins for 2534 passengers, but if need be, can host up to 2700 passengers–the 1030 crew members excluded. The other ship in the habour, Costas Firenze, has 2116 cabins for up to 5078 passengers (two times the Titanic) and provides for a crew of about 1300 members.
Due to Covid-19, the ships are far from fully booked. In my case the occupation rate was about 40%, a bit more than 1000 people.
Life at sea on this German-operated ship is best compared to Club holidays in Germany3:
- room service takes care of cabins twice per day
- all inclusive all you can eat and drink
- about 5 restaurants and several bars
- Döner Kebab at the poolside :sweat_smile:
- SPA area
- fitness club with courses, volleyball court and running area
- entertainment program in the lounge, the two theatres or on the open-air stage with shows that involve the dancers and acrobats belonging to the crew and guest artists (stand-up, magician)
- casino (open when in international waters)
- a few shops for shopping (VAT-free!)
- kids club
- organised day trips (bus, bike and boat tours, etc.)
- mostly German-speaking crew
- 99% German-speaking guests/passengers (I spotted two Dutch)
Consequently, a cruise on this ship is the perfect fit for all those who would like to hang out with Germans, have German bread and bread rolls for every meal, enjoy Sauerkraut, Klöße, Currywurst and Döner Kebab, but at the same time rather prefer a more Mediterranean climate than what Germany can typically offer! Kind of German holidays outside of Germany.
Gallery
With 1000 German passengers on board, it is easy to make pictures of the scenic locations without people: At 8 PM, everyone is at dinner! Let me take you on a tour.

Pool area.

Running path (opening hours: 6 to 8 AM).

Salon area.

Salon area II.

Salon area III.

Midnight snackbar: burgers, Belgium fries, cakes, etc.

Theatre of the ship.

Sport court.
I have a few more impressions taken at daytime.

View from Deck 12 to the family pool area.

Spa area.
-
I know because during some show on the deck, the moderator has asked the question who has been on a cruise before and many hands were raised. ↩︎
-
There was a meetup of single travellers on the ship. However, I didn’t use the occasion to ask them whether they were really widowed. :see_no_evil: ↩︎
-
Not that I have ever done club holidays in Germany–but that’s how I imagine it! ↩︎
Podcast 07×10 Resumen y final de temporada
Finalizamos la séptima temporada de video-audios de la Asociación KDE España. Así que me complace compartir con vosotros que ya está disponible el podcast 07×10 Resumen y final de temporada que ha contado con la producción de David Marzal, un interesante programa que sirve tanto como recordatorio como revisión de todo lo que hemos hablado a lo largo de más de un año.
Podcast 07×10 Resumen y final de temporada
Bienvenidos a un nuevo episodio de los podcast de KDE España, el
décimo y último de esta séptima temporada y que, como es tradición, es una resumen de la misma.
En esta ocasión tenemos a David Marzal como productor, uno de los podcasters más prolíficos y eficientes que conozco, miembro de KDE España y de Residuo Cero Región de Murcia.
Y, para variar, los entrevistados fueron:
- Rubén Gómez: miembro de KDE España, de HackLab Almería y de Document Foundation en la labor de presentador.
- Baltasar Ortega (un servidor): editor de KDE Blog, secretario de KDE España, miembro de GNU/Linux València y de KDE e.V.
De esta forma, a lo largo de la hora y veinte minutos que duró el podcast revisamos todos los episodios de la séptima temporada y comentamos el estado actual de algunos de esos proyectos de Software Libre, haciendo hincapié en lo más importante de ellos: las personas.
Los enlaces de interés del podcast son:
- Web: https://www.kde-espana.org
- Mastodon: @kde_es@tube.kockatoo.org
- Canal Planet de Telegram: https://t.me/kdeplanet_es
- Grupo de Telegram: https://t.me/kde_canasbravas
- Peertube: https://tube.kockatoo.org/a/kde_es
- Podcast KDE Express: https://kdeexpress.gitlab.io/
Aprovecho para poneros el enlace a la lista de los podcast de KDE España de Youtube, por si alguien quiere hacer una maratón y ver como evolucionamos a lo largo del tiempo.
Como siempre, esperamos vuestros comentarios que os aseguro que son muy valiosos para los desarrolladores, aunque sean críticas constructivas (las otras nunca son buenas para nadie). Así mismo, también nos gustaría saber los temas sobre los que gustaría que hablásemos en los próximos podcast.

Playing with Shelly
For xmass I got few Shelly lamps to play with. Shelly lamps are simple IoT devices. Super easy to install, configure and use. The Youtube is full with instructions on what can be done with these smart lamps. Naturally my main motivation was to figure out how to hack these devices and how ready my openSUSE servers are with tools and services (spoiler: they are ready)
Look daddy no cloud
Needless to say that like most smart home automation devices the Shelly lamps can be operated via the Shelly cloud. I may cover that area in the next post. But now I am interested in what can be done without the cloud. After all, one big selling point of the Shelly devices is that they are fully operable and functional even without Internet connection just on a WiFi LAN. It means that if I am concerned about the security of my home infrastructure I have an option not to expose my smart devices.
Home Assistant for a Newbie
Navegador Lagrange para Gemini ahora disponible para Android
Ya hay disponible una APK para Android del navegador Lagrange para protocolo Gemini, aunque en una versión Alfa muy temprana de desarrollo

Jaakko Keränen, el desarrollador del navegador Lagrange, para protocolos Gemini, hace un tiempo estaba tratando de portar el código para estar disponible para Android y arquitecturas ARM.
Ya ha anunciado que se ha publicado una primera versión Alfa de la aplicación APK solo para arquitecturas ARM de32 bits.
En el anuncio, nos deja claro que es una muy primera versión de desarrollo, por lo que tengamos en cuenta que pueden faltar de implementar nuevas funcionalidades, algunas pueden no funcionar como se espera y otras simplemente harán que «crashee» la aplicación.
Otros fallos con los que cabe contar, es que solo funciona en orientación vertical, no tiene integración con el sistema operativo y algunos otros problemas, pero quería publicar una primera versión de trabajo para ver cómo se desenvolvía y para que hubiera gente que la probara.
También advierte que esta primera versión es una prueba a sí mismo, para ver si puede crear una APK que se pueda instalar, ya que no es usuario ni desarrollador de Android.
El desarrollo lo ha realizado mediante Android Studio en un sistema Xubuntu y después de lidiar con alguna contrariedad pudo hacer funcionar el entorno para desarrollar Lagrange para Android.
Estos son algunos de los errores encontrados que tiene esta APK y que pronto se irán puliendo:
- Fallo al crear un marcador por primera vez
- Bloqueo al abrir una imagen
- Fallo al actualizar los feeds suscritor
- Fallo al ver la página de prueba de Emoji
- Crash al abrir la configuración
- Los enlaces web no se abren (necesitan un SDL más reciente)
Aunque todavía, como admite su desarrollador, la versión está en fase inicial de desarrollo, es bueno ver que una versión para Android está disponible, lo que ayudará a difundir y extender y utilizar más el protocolo Gemini.
Te dejo un enlace al anuncio oficial en su blog desde donde podrás descargar la APK y si quieres instalarla en tu teléfono y reportar fallos que encuentres.
Enlaces de interés
- https://gmi.skyjake.fi/gemlog/2021-12_android-alpha.gmi
- gemini://skyjake.fi/gemlog/2021-12_android-alpha.gmi (enlace para abrir con un navegador compatible con el protocolo Gemini, por ejemplo Lagrange)

Jekyll: Import Disqus comments for Staticman
For some years already, I try to rely for this website on less external resources and avoid ad-powered services to improve the privacy for my dear readers.
Recently, I removed the comments provided by Disqus from this blog, because Disqus introduced too much data sharing with many third parties. Norway just fined this year Disqus 2,5 Mio Euro for tracking without legal basis.
Please find hereafter some tips on how to export comments from Disqus and display them in a privacy-friendly way in your Jekyll blog.
Export Disqus Comments to JSON and YAML
- Disqus documents the export and export format at https://docs.disqus.com/developers/export/
-
Navigate to http://disqus.com/admin/discussions/export/ to export your comments to XML format.
The XML has principally 3 parts: meta data, a list with webpages and a list with comments that are linked each to a webpage (via a Disqus identifier) and possibly a parent comment in case the comment is a reply.
For use within Jekyll, I need to restructure the data and have a list of comments for each webpage by my own identifier (e.g. post slug) and convert everything to a format that Jekyll can handle, hence YAML, JSON, CSV, or TSV. I choose YAML.
-
Install the linux tool
xqto manipulate XML files and export to JSON and the tooljq.xqis basically a wrapper ofjq.pip install xqDownload binaries of jq here: https://stedolan.github.io/jq/download/
-
I convert then the Disqus XML export into a JSON file with the code in
export-disqus-xml2json.sh - Then, I pipe the output through
import-json-yaml.rbto split the list of comments into individual files for easy consumption by Jekyll.
# file: 'export-disqus-xml2json.sh'
#!/usr/bin/env sh
xq '.disqus | .thread as $threads | .post | map(select(.isDeleted == "false")) | map(.thread."@dsq:id" as $id | ($threads[] | select(."@dsq:id" == $id)) as $thread | {id: ("disqus-"+."@dsq:id"), date: .createdAt, slug: ($thread.id | tostring | gsub("/$";"") | split("/") | last), name: (if .author.name == "Robert" then "Robert Riemann" else .author.name end), avatar: .author | (if has("username") and .username != "rriemann" then "https://disqus.com/api/users/avatars/"+.username+".jpg" else null end), email: .author | (if has("username") and .username == "rriemann" then "my@mail.com" else null end), message, origin: ($thread.link | tostring | gsub("^https://blog.riemann.cc";"")), replying_to: (if has("parent") then ("disqus-"+.parent."@dsq:id") else null end)})' "$@"
Example comment from the JSON list:
{
"id": "disqus-4145062197",
"date": "2018-10-14T22:14:58Z",
"slug": "versioning-of-openoffice-libreoffice-documents-using-git",
"name": "Robert Riemann",
"avatar": null,
"email": "my@mail.com",
"message": "<p>I agree, it is not perfect. I have no solution how to keep the noise out of git.</p>",
"origin": "/2013/04/23/versioning-of-openoffice-libreoffice-documents-using-git/",
"replying_to": "disqus-4136593561"
}
The script import-json-yaml.rb takes each comment and puts it in YAML format with a unique filenname in the folder named after the slug.
# file: 'import-json-yaml.rb'
#!/usr/bin/env ruby
require 'json'
require 'yaml'
require 'fileutils'
require 'date'
data = if ARGV.length > 0 then
JSON.load_file(ARGV[0])
else
JSON.parse(ARGF.read)
end
data.each do |comment|
FileUtils.mkdir_p comment['slug']
File.write "#{comment['slug']}/#{comment['id']}-#{Date.parse(comment['date']).strftime('%s')}.yml", comment.to_yaml
end
The output with tree looks like:
_data
├── comments
│ ├── announcing-kubeplayer
│ │ ├── disqus-113988522-1292630400.yml
│ │ └── disqus-1858985256-1424044800.yml
│ ├── requires-owncloud-serverside-backend
│ │ ├── disqus-41270666-1269302400.yml
│ │ ├── disqus-41273219-1269302400.yml
...
Display Comments in Jekyll
Those comments are accessible in jekyll posts/pages via site.data.comments[page.slug]
Most helpful for the integration of comments to Jekyll was the post https://mademistakes.com/mastering-jekyll/static-comments-improved/.
<!-- file: 'my-comments.html' -->
{% assign comments = site.data.comments[page.slug] | sort %}
{% for comment in comments %}
{% assign index = forloop.index %}
{% assign replying_to = comment[1].replying_to | to_integer %}
{% assign avatar = comment[1].avatar %}
{% assign email = comment[1].email %}
{% assign name = comment[1].name %}
{% assign url = comment[1].url %}
{% assign date = comment[1].date %}
{% assign message = comment[1].message %}
{% include comment index=index replying_to=replying_to avatar=avatar email=email name=name url=url date=date message=message %}
{% endfor %}
<!-- file: 'comment' -->
<article id="comment{% unless include.r %}{{ index | prepend: '-' }}{% else %}{{ include.index | prepend: '-' }}{% endunless %}" class="js-comment comment {% if include.name == site.author.name %}admin{% endif %} {% unless include.replying_to == 0 %}child{% endunless %}">
<div class="comment__avatar">
{% if include.avatar %}
<img src="{{ include.avatar }}" alt="{{ include.name | escape }}">
{% elsif include.email %}
<img src="https://www.gravatar.com/avatar/{{ include.email | md5 }}?d=mm&s=60" srcset="https://www.gravatar.com/avatar/{{ include.email | md5 }}?d=mm&s=120 2x" alt="{{ include.name | escape }}">
{% else %}
<img src="/assets/img/avatar-60.jpg" srcset="/assets/img/avatar-120.jpg 2x" alt="{{ include.name | escape }}">
{% endif %}
</div>
<div class="comment__inner">
<header>
<p>
<span class="comment__author-name">
{% unless include.url == blank %}
<a rel="external nofollow" href="{{ include.url }}">
{{ include.name }}
</a>
{% else %}
{{ include.name }}
{% endunless %}
</span>
wrote on
<span class="comment__timestamp">
{% if include.date %}
{% if include.index %}<a href="#comment{% if r %}{{ index | prepend: '-' }}{% else %}{{ include.index | prepend: '-' }}{% endif %}" title="link to this comment">{% endif %}
<time datetime="{{ include.date | date_to_xmlschema }}">{{ include.date | date: '%B %d, %Y' }}</time>
{% if include.index %}</a>{% endif %}
{% endif %}
</span>
</p>
</header>
<div class="comment__content">
{{ include.message | markdownify }}
</div>
</div>
</article>
Receiving New Comments
Like explained in https://mademistakes.com/mastering-jekyll/static-comments/, the software https://staticman.net/ allows to feed POST HTTP requests to Github and Gitlab pull requests, so that comments can be added automatically. Of course, the website requires after each time a rebuild.
I had much trouble to setup Staticman. Eventually, I decided to use a Ruby CGI program that emails me the new comment as an attachment. I like Ruby very much. :wink: Once I figured out how to use the Gitlab API wrapper, I may also use pull requests instead of email attachments.
# file: 'index.rb'
#!/usr/bin/env ruby
Gem.paths = { 'GEM_PATH' => '/var/www/virtual/rriemann/gem' }
require 'cgi'
require 'yaml'
require 'date'
require 'mail'
cgi = CGI.new
# rudimentary validation
unless ENV['HTTP_ORIGIN'] == 'https://blog.riemann.cc' and
ENV['CONTENT_TYPE'] == 'application/x-www-form-urlencoded' and
ENV['REQUEST_METHOD'] == 'POST' and
cgi.params['email']&.first&.strip =~ URI::MailTo::EMAIL_REGEXP and
cgi.params['age']&.first == '' then # age is a bot honeypot
print cgi.http_header("status" => "FORBIDDEN")
print "<p>Error: 403 Forbidden</p>"
exit
end
output = Hash.new
date = DateTime.now
output['id'] = ENV['UNIQUE_ID']
output['date'] = date.iso8601
output['updated'] = date.iso8601
output['origin'] = cgi.params['origin']&.first
output['slug'] = cgi.params['slug']&.first&.gsub(/[^\w-]/, '') # some sanitizing
output['name'] = cgi.params['name']&.first
output['email'] = cgi.params['email']&.first&.downcase&.strip
output['url'] = cgi.params['url']&.first
output['message'] = cgi.params['message']&.join("\n").encode(universal_newline: true)
output['replying_to'] = cgi.params['replying_to']&.first
#Mail.defaults do
# delivery_method :sendmail
#end
Mail.defaults do
delivery_method :smtp, address: "smtp.domain", port: 587, user_name: "smtp_user", password: "smtp_password", enable_starttls_auto: true
end
mail = Mail.new do
from 'no-reply@domain' # 'rriemann'
to 'comments-recipient@domain' # ENV['SERVER_ADMIN']
reply_to output['email']
header['X-Blog-Comment'] = output['slug']
subject "New Comment from #{output['name']} for #{cgi.params['title']&.first}"
body <<~BODY
Hi blog author,
a new comment from #{output['name']} for https://blog.riemann.cc#{output['origin']}:
#{output['message']}
BODY
add_file(filename: "#{output['id']}-#{date.strftime('%s')}.yml", content: output.to_yaml)
end
mail.deliver
if mail.error_status then
print cgi.http_header("status" => "SERVER_ERROR")
cgi.print <<~RESPONSE
<p><b>Error: </b> #{mail.error_status}</p>
<p>An error occured. Please try again later.</p>
<p><a href="javascript:history.back()">Go back</a></p>
RESPONSE
else
print cgi.http_header
cgi.print <<~RESPONSE
<p><b>Thank you</b> for your fedback! Your comment is published after review.</p>
<p><a href="#{output['origin']}">Back to the previous page</a></p>
RESPONSE
end
To make it work with Apache, you may need to add these lines to the Apache configuration (could be a .htaccess file):
DirectoryIndex index.html index.rb
Options +ExecCGI
SetHandler cgi-script
AddHandler cgi-script .rb
Foto de grupo de Akademy-es 2021 #akademyes
No hay evento que se precie que no tenga su correspondiente imagen para la posteridad, y los eventos virtuales no son una excepción. Bienvenidos a una breve pero significativa entrada en la que os presento la foto de grupo de Akademy-es 2021 que se ha tomado, evidentemente, utilizando las limitaciones del espacio que nos ofrecen las ventanas de webconferencia.
Foto de grupo de Akademy-es 2021 #akademyes
Antes, cuando las Akademy-es eran virtuales, era una tradición (que se inició en Almería 2017 y que continuó en Valencia 2018 y siguió en Vigo 2019) que el mismo día de la realización de la misma yo publicara la ansiada foto de grupo.
Este año ha sido una excepción (por diversos motivos) y me ha costado un poco más ponerla ya que he de reconocer que ha sido un diciembre muy movido.
Esta captura de pantalla, que no en realidad foto de grupo, se ha realizado con solo los participantes que pusieron su webcam en el momento de tomar la instantánea y que el sistema permite mostrar en solo una pantalla.. Es por ello que en esta ocasión más que nunca se puede decir que son todos los que están pero no están todos los que son.
Pero, o me enrollo más y os dejo ya la foto de grupo de Akademy-es 2021 de Vigo, en la que no están todos los que son pero si que son todos los que están. Pinchad en la imagen para verlo más grande.

¿Qué es el Akademy-es?
Akademy-es es el encuentro anual de desarrolladores, colaboradores y usuarios de KDE en España, que se celebra desde el año 2006 en distintas ciudades del territorio español y con esta se llegará a la decimasexta edición, lo cual significa una cifra más que aceptable.
Os recuerdo que el registro y la asistencia a las Akademy-es es libre y gratuito, es decir, no te cuesta nada. Se hace únicamente para contar asistente y para organizar la tradicional cena del sábado (que esperamos que vuelva con fuerza).
Más información: KDE España
Pi-Hole the Easy Way
Natal de NERD: Lançado openCV 4.5.5

Versão 4.5.5 da biblioteca openCV foi lançada com diversas novidades. Como suporte a áudio na API VideoCapture, otimização ao RISC-V, suporte ao OpenVINO 2021.4.2 LTS release atualização do exemplo do módulo videoio da câmera realsense e muito mais. Todos os detalhes aqui: https://github.com/opencv/opencv/wiki/ChangeLog


2021年を振り返って
この記事は openSUSE Advent Calendar の25日目です。
今年も openSUSE Advent Calendar にご参加頂きありがとうございました。みなさん忙しかったからか、今年は13日目で力尽きてしまいました。
最終日は openSUSE 関連の活動を振り返ってみようと思います。
1〜5月
うーん。何かを行った記録がありません。15.3 向けの開発をしていたかもしれません。
6月
openSUSE Leap 15.3 がリリースされました。今年はリリースパーティーも mini Summit もできなかったので、来年はしたいですね。
8月
openSUSE.Asia Summit をオンラインで開催しました。もともとは昨年にインドで開催する予定でしたが、インドチームによるオンライン開催になりました。
https://events.opensuse.org/conferences/oSAS21
8月末に開催された Open Developers Conference (ODC) では、Cross Distro Developers Camp の枠で Leap 15.3 とリリースモデルの話をしました。
https://speakerdeck.com/ftake/opensuse-leap-15-dot-3-toririsumoderufalsehua
9月〜11月
同じく活動の記録が…。
12月
今年も Advent Calendar を作成しました。序盤は勢い良くスタートしましたが…途中でばったり力尽きてしまった感じになってしまいました。
大晦日には Geeko Magazine Special Edition を久しぶり発行します。2年ぶりとは思えないページ数になっています。オンラインイベントが続くため、頒布機会がなかなか確保できなさそうですが、機会があればお手にとって見たください。
来年は?
今年は仕事が忙しく、Mozc の最新バージョンへのアップデートなど、いろいろやりかけになってしまいました。なんとか時間を取れるようにしたいなと思います。