Commit 62e97f75 authored by rgaudin's avatar rgaudin
Browse files

Fixed #190: Added support for RasClock Hardware Clock module

Use Hardware Clock Module if present
Web UI to configure at http://time.fqdn (protected using image's admin/pass)

Setup instructions on the Wiki:
parent ba2876ba
* Support for external hardware clock module (rasclock)
* Rebranded as Kiwix-plug installer
* ZIM files in cache folder can be used with Kiwix Desktop
......@@ -60,6 +60,7 @@ aflatoun_server_name: aflatoun.{{ hostname }}
edupi_server_name: edupi.{{ hostname }}
wikifundi_server_name: wikifundi.{{ hostname }}
sites_server_name: sites.{{ hostname }}
time_server_name: time.{{ hostname }}
kiwix_fqdn: kiwix.{{ fqdn }}
kalite_fqdn: khanacademy.{{ fqdn }}
......@@ -67,6 +68,7 @@ aflatoun_fqdn: aflatoun.{{ fqdn }}
edupi_fqdn: edupi.{{ fqdn }}
wikifundi_fqdn: wikifundi.{{ fqdn }}
sites_fqdn: sites.{{ fqdn }}
time_fqdn: time.{{ fqdn }}
# content & features
captive_portal: true
......@@ -35,6 +35,9 @@
# master, rename
- role: kiwix
# master, rename (nginx hostname), reconfigure (password)
- role: clock
# master, rename
- role: captive_portal
when: ansible_local.config.captive_portal|bool
#!/usr/bin/env python
# -*- coding: utf-8 -*-
''' hardware clock management UI
- set HW clock using system time
- set HW clock manually
- set system time using HW clock '''
import re
import urllib
import subprocess
header = u"""<html><head><meta charset="utf-8"><style type="text/css">th { text-align: left; }</style></head>"""
body = u"""<body>
<h1><a href="/">Pi Time</a></h1>
<tr><th>System date</th><td>{system_time}</td></tr>
<tr><th>Hardware date</th><td>{hardware_time}</td></tr>
<h1>Configure Hardware Clock (online)</h1>
<p>Use this option if the hardware clock is far behind the system one or unavailable.</p>
<p>This is what you would do to <strong>configure a newly plugged hardware clock</strong> using a <strong>connected Pi</strong>.</p>
<form action="/sys2hw" method="GET">
<input type="submit" value="Copy System Time into Hardware Clock" />
<h1>Configure Hardware Clock (offline)</h1>
<p>Use this option if neither the Hardware Clock nor the System clock is right.</p>
<p>This is what you would do to <strong>configure a newly plugged hardware clock</strong> using a <strong>not-connected Pi</strong>.</p>
<form action="/manual2hw" method="GET">
<input name="datetime" type="text" pattern="[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]" required="required" placeholder="2017-12-01 20:30" />
<input type="submit" value="Save this time into Hardware Clock" />
<h1>Update System Clock</h1>
<p>Use this option if the system clock is far behind the hardware one.</p>
<p>This step is not required as this is done upon system startup but can help you check the process.</p>
<form action="/hw2sys" method="GET">
<input type="submit" value="Update System Time with Hardware Clock one" />
footer = u"</html>"
date_bin = '/bin/date'
hwclock_bin = '/sbin/hwclock'
tdctl_bin = '/usr/bin/timedatectl'
def get_output(command):
return subprocess.check_output(['sudo'] + command).strip()
except Exception as exp:
return "ERROR: {}".format(exp)
def application(env, start_response):
output = ""
if env['REQUEST_URI'] == '/sys2hw':
# write system datetime into hardware clock
output = get_output([hwclock_bin, '-w'])
if env['REQUEST_URI'] == '/hw2sys':
# set system datetime using hardware clock
output = get_output([hwclock_bin, '-s'])
if env['REQUEST_URI'].startswith('/manual2hw'):
# write a manual datetime into hardware clock
dt = urllib.unquote_plus(env['QUERY_STRING']) \
# disable ntp otherwise we can't set manual date
get_output([tdctl_bin, 'set-ntp', 'no'])
# set manual date
output = get_output([tdctl_bin, 'set-time', dt])
# re-enable ntp. if online, will overwrite our manual datetime
get_output([tdctl_bin, 'set-ntp', 'yes'])
except Exception as exp:
output = "ERROR: {}".format(exp)
context = {
'output': '<p style="color: blue; font-weight: bold;">{}</p>'
'system_time': get_output(
[date_bin, '+"%Y-%m-%d %H:%M:%S.000000%z"'])[1:-1],
'hardware_time': get_output([hwclock_bin, '-r']),
start_response('200 OK', [('Content-Type', 'text/html')])
page = header + body.format(**context) + footer
return [page.encode('utf-8')]
autoload = false
uid = ideascube
gid = ideascube
socket = :3032
wsgi-file = /var/www/clock/
process = 1
plugin = python
- name: add dtoverlay to boot config
path: /boot/config.txt
line: 'dtoverlay=i2c-rtc,pcf2127-rtc,pcf2127'
state: present
tags: master
- name: fix hclock-set
path: /lib/udev/hwclock-set
regexp: '-e /run/systemd/system'
line: 'if [ 1 -eq 0 ] ; then'
tags: master
- name: fix hclock-set 2
path: /lib/udev/hwclock-set
regexp: '--systz'
state: absent
tags: master
- name: create password file for clock
USERNAME: "{{ admin_account }}"
PASSWORD: "{{ admin_password }}"
shell: python -c 'import os ; from random import choice; from crypt import crypt ; from string import ascii_letters, digits ; symbols = ascii_letters + digits ; salt = choice(symbols) + choice(symbols) ; print("{u}:{p}\n".format(u=os.environ.get("USERNAME", "admin"), p=crypt(os.environ.get("PASSWORD", "admin"), salt)))' > /var/www/.admin-passwd
tags: ['setup', 'reconfigure']
- name: Create /var/www/clock folder
path: /var/www/clock
owner: "{{ username }}"
group: ideascube
mode: 0755
state: directory
tags: master
- name: Copy clock script to /var/www/clock
dest: /var/www/clock/
owner: "{{ username }}"
group: ideascube
mode: 0755
notify: restart uwsgi
tags: master
- name: Add UWSGI specific configuration file
src: uwsgi.ini
dest: /etc/uwsgi/apps-available/clock.ini
owner: root
group: root
mode: 0644
tags: master
- name: Add nginx specific configuration file
src: nginx.conf.j2
dest: /etc/nginx/sites-available/clock
owner: root
group: root
mode: 0644
tags: ['master', 'rename']
- name: Enable clock.ini for UWSGI
src: /etc/uwsgi/apps-available/clock.ini
dest: /etc/uwsgi/apps-enabled/clock.ini
state: link
notify: restart uwsgi
tags: master
- name: Enable clock vhost
src: /etc/nginx/sites-available/clock
dest: /etc/nginx/sites-enabled/clock
state: link
notify: restart nginx
tags: master
server {
server_name {{ time_fqdn }} {{ time_server_name }};
location / {
auth_basic "Please log-in";
auth_basic_user_file /var/www/.admin-passwd;
include uwsgi_params;
proxy_connect_timeout 60;
proxy_send_timeout 60;
proxy_read_timeout 60;
......@@ -48,11 +48,15 @@
aflatoun_server_name: aflatoun.{{hostname}}
edupi_server_name: edupi.{{hostname}}
wikifundi_server_name: wikifundi.{{hostname}}
sites_server_name: sites.{{hostname}}
time_server_name: time.{{hostname}}
kiwix_fqdn: kiwix.{{ fqdn }}
kalite_fqdn: khanacademy.{{fqdn}}
aflatoun_fqdn: aflatoun.{{fqdn}}
edupi_fqdn: edupi.{{fqdn}}
wikifundi_fqdn: wikifundi.{{fqdn}}
sites_fqdn: sites.{{fqdn}}
time_fqdn: time.{{fqdn}}
name: Update names and fqdns for all projects
- lineinfile:
......@@ -8,11 +8,11 @@
"expanded_size": 1862270976
"pibox_base_image": {
"url": "{mirror}/plug/base/",
"name": "",
"url": "{mirror}/plug/base/",
"name": "",
"root_partition_size": 7000000000,
"checksum": "2e4ad4528d1d82392a382e6a457a3d858f650da6e7578cf78c267b4883ecec6b",
"archive_size": 2648030117,
"checksum": "c23e3ea6cd2fbeab1e4b73ae20c50a734d9abe9e80b30aa5cac4a151abd4d6b1",
"archive_size": 2619927626,
"expanded_size": 8000000000
"aflatoun_content": {
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment