diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 7274ad1..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/documentation/build/
-/source/arduino/build/
diff --git a/README.md b/README.md
index 863398e..ec54111 100644
--- a/README.md
+++ b/README.md
@@ -21,126 +21,24 @@ includes a field indicating whether the space is open or closed.
## Arduino
-### Arduino: Hardware
-
-We measure the door's lock state using a reed switch. The switch is installed at
-the bottom of hole for the deadbolt and gets triggered by a magnet glued to the
-tip of the deadbolt.
-
-The reed switch is connected to pin 0 and ground on an Arduino MKR1000. The
-software sets up pin 0 (by default) with a pull up resistor. This prevents
-currents induced into the long wire between switch an Arduino to trigger the pin
-state.
-
-A second component connected to the MKR1000 is an Arduino MKR RGB Shield that
-provides a 7x12 RGB LED matrix. It is used to give visual feedback on the
-software's current state.
-
-While trying to connect to the configured WLAN, a vertical yellow line
-oscillates between both sides of the display. When connecting to the WLAN fails,
-a red cross is displayed. When the room is open all LEDs are green, and when it
-is closed all LEDs are turned off (nobody should be there to see it anyway).
-
-The following images is a schematic showing the connections between the three
-components mentioned.
-
-![a schematic of Arduino MKR1000 and all components physically connected to it, as described in the text](documentation/arduino.png)
-
-The Arduino is powered by a USB charger.
-
-If you need to change the schematic, adapt `documentation/arduino.tex` and run
-`make`. This requires the following software and TeX libraries to be installed
-on your systems:
-
-- pdflatex
-- convert from ImageMagick
-- tikz
-- circuitikz
-
-### Arduino: Setup and Configuration
-
-The sketch for our Arduino is located in `sources/arduino`. Before we flash the
-Arduino with the sketch, we should adapt `config.h`. We can use either the
-[Arduino IDE](https://www.arduino.cc/en/software) or the provided Makefile to
-verify or flash the sketch.
-
-For the Makefile to work,
-[Arduino CLI](https://arduino.github.io/arduino-cli/latest/) has to be
-installed. Use `make` for verification and `make install` to flash the board,
-which needs to be connected to our PC via USB cable.
-
-Verifying and flashing the sketch requires the installation of several
-libraries.
-
-First we need the core for the family of boards Arduino MKR1000
-belongs to: Arduino SAMD Boards (or `arduino:samd`). Installation instructions
-for [Arduino IDE](https://www.arduino.cc/en/guide/cores) and
-[Arduino CLI](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_core_install/)
-can be found in the respective documentation.
-
-Then we need to make sure the following libraries are installed:
-
-- ArduinoGraphics
-- Arduino_MKRRGB
-- uTimerLib
-- WiFi101
-
-Installed instructions for
-[Arduino IDE](https://www.arduino.cc/en/guide/libraries) and
-[Arduino CLI](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_lib_install/)
-can be found in the respective documentation.
-
-We assign a fixed IP address in the space's WiFi network to the Arduino's MAC
-address. The MAC address gets printed to `Serial` on each start up. Once
-connected to the configured WLAN, the IP address is also printed to `Serial`.
-(TODO: MENTION WHERE TO FIND DOCUMENTATION FOR OUR DHCPD.)
-
-### Arduino: UDP API
-
-Once the Arduino has started successfully, it listens on the configured port
-(see [sources/arduino/config.h](sources/arduino/config.h) for the default port)
-for UDP packets and answers each incoming packet with the state of the door. The
-state is encoded as a one byte integer, where `0` stands for closed and `1`
-stands for open.
-
-In `scripts` is the Python 3 script `test_udp_api.py`, which we use to test the
-UDP API. It takes an IP address and optionally a port, queries the Arduino, and
-prints out the result.
-
-## In-Space Service
-
-## Internet Service
+## Python Script
+## API
## License
The authors of the Arduino code license the code under the AGPL 3.0 (see
-`LICENSE.AGPL`). The respective authors are listed in the preambles of each
-source file.
+`LICENSE.AGPL`). The respective authors are listed in the in the preambles of
+each source file.
## Dependencies
-The creation of the schematic image depends on the following libraries:
-
-*TikZ* published by the copyright holders under the GPL 2, the Free
-Documentation License, and the LaTeX Project Public License 1.3c
-
-*Circuitikz* published by the copyright holders under the GPL 2 and the LaTeX
-Project Public License
-
The Arduino code depends on the following libraries:
-*Arduino SAMD Boards* published by the copyright holders under the LGPL 2.1 or later.
+_Arduino Core_ published by the copyright holders under the LGPL 2.1 or later.
-*SPI Master Library for Arduino Zero* published by the copyright holders under
+_SPI Master Library for Arduino Zero_ published by the copyright holders under
the LGPL 2.1 or later.
-*ArduinoGraphics* published by the copyright holders under the LGPL 2.1 or
-later.
-
-*Arduino_MKRRGB* published by the copyright holders under the LGPL 2.1 or later.
-
-*uTimerLib* published by the copyright holders under the LGPL 3.
-
-*WiFi101* published by the copyright holders under the
+_Library for Arduino WiFi Shield_ published by the copyright holders under the
LGPL 2.1 or later.
diff --git a/documentation/Makefile b/documentation/Makefile
deleted file mode 100644
index f4a2f05..0000000
--- a/documentation/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c):
-#
-# Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-#
-# This file is part of the KrautStatus' Arduino code.
-#
-# The Clean CommonMark library is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# The Clean CommonMark library is distributed in the hope that it will be
-# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License along
-# with the Clean CommonMark library. If not, see
-# .
-
-.PHONY: all clean
-
-all: arduino.png
-
-clean:
- rm --recursive build
-
-arduino.png: arduino.tex build
- pdflatex -output-directory build arduino
- convert -density 300 build/arduino.pdf -trim -colorspace RGB arduino.png
-
-build:
- mkdir --parents build
diff --git a/documentation/arduino.png b/documentation/arduino.png
deleted file mode 100644
index efa3f65..0000000
Binary files a/documentation/arduino.png and /dev/null differ
diff --git a/documentation/arduino.tex b/documentation/arduino.tex
deleted file mode 100644
index 0ddf92f..0000000
--- a/documentation/arduino.tex
+++ /dev/null
@@ -1,74 +0,0 @@
-% Copyright (c):
-%
-% Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-%
-% This file is part of the KrautStatus' Arduino code.
-%
-% The Clean CommonMark library is free software: you can redistribute it and/or
-% modify it under the terms of the GNU Affero General Public License as
-% published by the Free Software Foundation, either version 3 of the License, or
-% (at your option) any later version.
-%
-% The Clean CommonMark library is distributed in the hope that it will be
-% useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
-% General Public License for more details.
-%
-% You should have received a copy of the GNU Affero General Public License along
-% with the Clean CommonMark library. If not, see
-% .
-\documentclass[border=1mm]{standalone}
-
-\usepackage{tikz}
-\usetikzlibrary{positioning}
-\usepackage{circuitikz}
-
-\begin{document}
-
-\begin{circuitikz}[
- chip/.style = {
- dipchip,
- hide numbers,
- no topmark,
- external pins width=0,
- num pins=14,
- }]
- \ctikzset{multipoles/dipchip/width=1.6}
-
- % Arduino MKR1000
- \node [chip] (MKR1000) {};
- % align=center enables line breaks in node text
- \node [align=center, rotate=90] at (MKR1000.center){Arduino\\MKR1000};
-
- \draw (MKR1000.n) -- ++(0,1) node[vcc]{};
- \draw (MKR1000.s) -- ++(0,-1) node[ground] {};
-
- \node [right, font=\tiny] at (MKR1000.bpin 4) {0};
-
- \node [left, font=\tiny] at (MKR1000.bpin 12) {VIN};
- \node [left, font=\tiny] at (MKR1000.bpin 11) {A3};
- \node [left, font=\tiny] at (MKR1000.bpin 10) {A4};
-
- % Arduino MKR RGB Shield
- \node[chip, right=of MKR1000] (RGB) {};
- % align=center enables line breaks in node text
- \node [align=center, rotate=90] at (RGB.center){Arduino\\MKR RGB Shield};
-
- \draw (RGB.s) -- ++(0, -1) node[ground]{};
-
- \node [right, font=\tiny] at (RGB.bpin 3) {VIN};
- \node [right, font=\tiny] at (RGB.bpin 4) {A3};
- \node [right, font=\tiny] at (RGB.bpin 5) {A4};
-
- \draw (MKR1000.bpin 12) -- (RGB.bpin 3);
- \draw (MKR1000.bpin 11) -- (RGB.bpin 4);
- \draw (MKR1000.bpin 10) -- (RGB.bpin 5);
-
- % Reed switch
- \draw (MKR1000.bpin 4) -- ++(-1, 0) to [normal open switch, name=R] ++(0, -2.96) node[ground]{};
-
- \node [rotate=90, above] at (R) {Reed Switch};
-\end{circuitikz}
-
-
-\end{document}
diff --git a/scripts/test_udp_api.py b/scripts/test_udp_api.py
deleted file mode 100755
index f0cc5a6..0000000
--- a/scripts/test_udp_api.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c):
-#
-# Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-#
-# This file is part of the KrautStatus' Arduino code.
-#
-# The Clean CommonMark library is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# The Clean CommonMark library is distributed in the hope that it will be
-# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License along
-# with the Clean CommonMark library. If not, see
-# .
-"""
-Test KrautStatus's UDP API for a given IP and (optionally) port.
-"""
-
-import argparse
-import ipaddress
-import socket
-
-def port(string):
- "Convert string to an unsigend integer that is a valid port number"
- port = int(string)
-
- if port < 0 or port > 65535:
- raise ValueError()
-
- return port
-
-def main():
- parser = argparse.ArgumentParser(__doc__)
-
- parser.add_argument("ip",
- metavar="IP",
- type=ipaddress.ip_address,
- help="IP address of the KrautStatus Arduino")
-
- parser.add_argument("port",
- nargs="?",
- metavar="PORT",
- default=12345,
- type=port,
- help="port that the KrautStatus Arduino listens to")
-
- args = parser.parse_args()
-
-
- receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- receiver.bind(('0.0.0.0', args.port))
- print(f'Listening on 0.0.0.0:{args.port}.')
-
- sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- # We do not use \0 or \1 here, so that we do not trigger ourselves when
- # testing the script locally (127.0.0.1)
- sender.sendto(b'\2', (str(args.ip), args.port))
- print(f'Sent null byte to {args.ip}:{args.port}.')
-
- while True:
- status, address = receiver.recvfrom(1)
-
- if not address[0] == str(args.ip):
- continue
-
- if status[0] == 0:
- print('Responded: door closed')
- return
-
- if status[0] == 1:
- print('Resondend: door open')
- return
-
-if __name__ == "__main__":
- main()
diff --git a/source/arduino/Makefile b/source/arduino/Makefile
deleted file mode 100644
index 0f54ea4..0000000
--- a/source/arduino/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c):
-#
-# Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-#
-# This file is part of the KrautStatus' Arduino code.
-#
-# The Clean CommonMark library is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# The Clean CommonMark library is distributed in the hope that it will be
-# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License along
-# with the Clean CommonMark library. If not, see
-# .
-
-.PHONY: all clean install
-
-all:
- arduino-cli compile --fqbn arduino:samd:mkr1000
-
-clean:
- rm --recursive build
-
-install:
- PORT=$(arduino-cli board list | \
- grep arduino:samd:mkr1000 | \
- sed -e 's/ .*//g'); \
- arduino-cli upload --fqbn arduino:samd:mkr1000 --port $$PORT
diff --git a/source/arduino/arduino.ino b/source/arduino/arduino.ino
deleted file mode 100644
index f6a3477..0000000
--- a/source/arduino/arduino.ino
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#include
-#include
-
-#include "config.h"
-#include "matrix.h"
-#include "sensor.h"
-#include "serial.h"
-#include "wifi.h"
-
-const unsigned long int MATRIX_UPDATE_FREQUENCY = 1;
-
-void setup() {
- serial_setup();
- matrix_setup();
-
- boolean wifi_result = matrix_show_scan_and_run(wifi_setup);
- if (!wifi_result) {
- matrix_show_failure();
- while(true);
- }
-
- sensor_setup();
-
- start_matrix_update();
-}
-
-void loop() {
- int packetSize = Udp.parsePacket();
- if (packetSize) {
- Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
- Udp.write(uint8_t(sensor_get_door_state()));
- Udp.endPacket();
- }
-}
-
-void start_matrix_update() {
- TimerLib.setInterval_us(matrix_update, 1000000 / MATRIX_UPDATE_FREQUENCY);
-}
-
-void matrix_update() {
- switch (sensor_get_door_state()) {
- OPEN:
- matrix_fill(GREEN);
- break;
- CLOSED:
- matrix_fill(BLACK);
- break;
- }
-}
diff --git a/source/arduino/config.h b/source/arduino/config.h
index 2cb04dc..49db287 100644
--- a/source/arduino/config.h
+++ b/source/arduino/config.h
@@ -19,15 +19,14 @@ You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see .
*/
-#include
-
// SSID and password of the WiFi network to which we broadcast the door lock's
// status.
-const char SSID[] = "";
-const char PASSWORD[] = "";
+char SSID[] = "";
+char PASSWORD[] = "";
+
// Port on which to listen for status requests
-const unsigned int SERVER_PORT = 12345;
+unsigned int PORT = 12345;
// Pin to which the reed switch is connected
-const uint8_t SENSOR_PIN = 0;
+uint8_t REED_PIN = 0;
diff --git a/source/arduino/wifi.cpp b/source/arduino/door_status.ino
similarity index 52%
rename from source/arduino/wifi.cpp
rename to source/arduino/door_status.ino
index a604d12..fd81a16 100644
--- a/source/arduino/wifi.cpp
+++ b/source/arduino/door_status.ino
@@ -25,43 +25,84 @@ with the Clean CommonMark library. If not, see .
#include "config.h"
+uint8_t OPEN = 1;
+uint8_t CLOSED = 0;
+
+int wifi_status = WL_IDLE_STATUS;
+
WiFiUDP Udp;
-void print_mac_address() {
- uint8_t mac[6];
- WiFi.macAddress(mac);
- Serial.print("MAC: ");
- for(unsigned int i = 5; i > 0; --i) {
- Serial.print(mac[i], HEX);
- Serial.print(":");
- }
- Serial.println(mac[0], HEX);
-}
+void setup() {
+ pinMode(REED_PIN, INPUT_PULLUP);
+
+ Serial.begin(9600);
+ while (!Serial);
-boolean wifi_setup() {
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("No WiFI shield present");
- return false;
+ // TODO: Create noShieldLoop with visual indication.
+ while (true);
}
- print_mac_address();
-
while (true) {
+ // TODO: Visually indicate that we are trying to connect
Serial.print("Connecting to SSID: ");
Serial.println(SSID);
+ status = WiFi.begin(SSID, PASSWORD);
- if (WiFi.begin(SSID, PASSWORD) == WL_CONNECTED) {
+ if (status == WL_CONNECTED) {
break;
}
+ // TODO: Visually indicate that we waiting for trying to connect again
delay(10000);
}
- Serial.println("Connect to WiFi");
- Serial.print("IP Address: ");
- Serial.println(WiFi.localIP());
+ printNetworkInfo();
+
+}
+
+void loop() {
+ int reed_state = digitalRead(REED_PIN);
+
+ int packetSize = Udp.readPacket();
+ if (packetSize) {
+ Udp.beginPacket(udp.remoteIP(), Udp.remotePort());
+
+ if (reed_state == HIGH) {
+ Udp.write(CLOSED);
+ } else {
+ Udp.write(OPEN);
+ }
+
+ Udp.endPacket();
+ }
+ // TODO: Visually indicate open/closed state.
+}
+
+
+void printNetworkInfo() {
+
+ Serial.print("Connect to WiFi");
+
+ IPAddress ip = WiFi.localIP();
+ Serial.print("IP Address: ");
+ Serial.println(ip);
+
+ byte mac[6];
+ WiFi.macAddress(mac);
+ Serial.print("MAC address: ");
+ for (int i = 5; i >= 0; --i) {
+ if (mac[i] < 16) {
+ Serial.print("0");
+ }
+
+ Serial.print(mac[i], HEX);
+
+ if (i > 0) {
+ Serial.print(":");
+ }
+ }
+ Serial.println();
- Udp.begin(SERVER_PORT);
-
- return true;
}
diff --git a/source/arduino/matrix.cpp b/source/arduino/matrix.cpp
deleted file mode 100644
index 38f9ace..0000000
--- a/source/arduino/matrix.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#include
-#include
-#include
-
-#include "matrix.h"
-
-
-const uint32_t RED = COLOR(255, 0, 0);
-const uint32_t YELLOW = COLOR(255, 255, 0);
-
-const unsigned long int SCAN_UPDATE_FREQUENCY = 25;
-
-void matrix_setup() {
- MATRIX.begin();
-}
-
-void matrix_show_failure() {
- MATRIX.stroke(RED);
- MATRIX.beginDraw();
- MATRIX.clear();
- MATRIX.line(0, 0, MATRIX.width() - 1, MATRIX.height() - 1);
- MATRIX.line(0, MATRIX.height() - 1, MATRIX.width() - 1, 0);
- MATRIX.endDraw();
-}
-
-uint8_t scan_timer = 0;
-
-void matrix_paint_scan() {
- // We paint a vertical line that oscillates between the left and right border
- // of the matrix. Going back and forth one pixel at a time gives us as period
- // of
- // 2 * width - 2
- // because we do not want to remain at the borders for one tick.
- scan_timer += 1;
- scan_timer %= 2 * MATRIX.width() - 2;
-
- uint8_t position = scan_timer;
- if(position >= MATRIX.width())
- position = 2 * MATRIX.width() - position - 2;
-
- MATRIX.beginDraw();
- MATRIX.clear();
- MATRIX.line(position, 0, position, 6);
- MATRIX.endDraw();
-}
-
-bool matrix_show_scan_and_run(bool(*thunk)()) {
- MATRIX.stroke(YELLOW);
- TimerLib.setInterval_us(matrix_paint_scan, 1000000 / SCAN_UPDATE_FREQUENCY);
-
- bool result = thunk();
-
- TimerLib.clearTimer();
- MATRIX.beginDraw();
- MATRIX.clear();
- MATRIX.endDraw();
-
- return result;
-}
-
-void matrix_fill(uint32_t color) {
- MATRIX.fill(color);
- MATRIX.beginDraw();
- MATRIX.clear();
- MATRIX.endDraw();
-}
diff --git a/source/arduino/matrix.h b/source/arduino/matrix.h
deleted file mode 100644
index 0b4029f..0000000
--- a/source/arduino/matrix.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#define COLOR(r, g, b) (r << 16 | g << 8 | b)
-
-const uint32_t BLACK = COLOR( 0, 0, 0);
-const uint32_t GREEN = COLOR( 0, 255, 0);
-
-void matrix_setup();
-
-void matrix_show_failure();
-
-bool matrix_show_scan_and_run(bool(*)());
-
-void matrix_fill(uint32_t color);
diff --git a/source/arduino/sensor.cpp b/source/arduino/sensor.cpp
deleted file mode 100644
index 173b04a..0000000
--- a/source/arduino/sensor.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#include
-
-#include "sensor.h"
-#include "config.h"
-
-void sensor_setup() {
- pinMode(SENSOR_PIN, INPUT_PULLUP);
-}
-
-door_state sensor_get_door_state() {
- if (digitalRead(SENSOR_PIN) == HIGH)
- return OPEN;
- return CLOSED;
-}
diff --git a/source/arduino/sensor.h b/source/arduino/sensor.h
deleted file mode 100644
index e6e5d4b..0000000
--- a/source/arduino/sensor.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-typedef enum {
- CLOSED = 0,
- OPEN = 1
-} door_state;
-
-void sensor_setup();
-
-door_state sensor_get_door_state();
diff --git a/source/arduino/serial.cpp b/source/arduino/serial.cpp
deleted file mode 100644
index d394e4d..0000000
--- a/source/arduino/serial.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#include
-
-void serial_setup() {
- Serial.begin(9600);
-}
diff --git a/source/arduino/serial.h b/source/arduino/serial.h
deleted file mode 100644
index 4e9ed58..0000000
--- a/source/arduino/serial.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-void serial_setup();
diff --git a/source/arduino/wifi.h b/source/arduino/wifi.h
deleted file mode 100644
index 17185b6..0000000
--- a/source/arduino/wifi.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Copyright (c):
-
-Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
-
-This file is part of the KrautStatus' Arduino code.
-
-The Clean CommonMark library is free software: you can redistribute it and/or
-modify it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or (at your
-option) any later version.
-
-The Clean CommonMark library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-for more details.
-
-You should have received a copy of the GNU Affero General Public License along
-with the Clean CommonMark library. If not, see .
-*/
-
-#include
-#include
-
-extern WiFiUDP Udp;
-
-bool wifi_setup();
diff --git a/source/nodemcu/statusclient/certs.template b/source/nodemcu/statusclient/certs.template
deleted file mode 100644
index 07679bb..0000000
--- a/source/nodemcu/statusclient/certs.template
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * file: certs.template
- * desc: This file is part of the Krautspace Doorstatus project. It contains
- * certificates for the statusclient.ino programm, that runs on a NodeMCU
- * with a ESP8266 chip.
- *
- * Replace the comments in certificate sections with our own certificates.
- */
-
-
-const char SERVER_CERT[] PROGMEM = R"EOF(
------BEGIN CERTIFICATE-----
-/*
- * lace for the public server certificate to authenticate the doorstatus
- * server.
- */
------END CERTIFICATE-----
-)EOF";
-
-const char CLIENT_CERT[] PROGMEM = R"EOF(
------BEGIN CERTIFICATE-----
-/*
- * Place for the clients (this program) public certificate to authenticate
- * client against the server.
- */
------END CERTIFICATE-----
-)EOF";
-
-const char CLIENT_KEY[] PROGMEM = R"EOF(
------BEGIN CERTIFICATE-----
-/*
- * Place for the clients private key file.
- */
------END CERTIFICATE-----
-)EOF";
-
diff --git a/source/nodemcu/statusclient/config.h b/source/nodemcu/statusclient/config.h
deleted file mode 100644
index b078888..0000000
--- a/source/nodemcu/statusclient/config.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * file: config.h
- */
-
-/* endpoint */
-#define SERVER_URL "status.kraut.space"
-#define SERVER_PORT 10001
-
-/* serial interface settings */
-#define BAUD_RATE 115200
-#define DEBUG true
-
-/* frequence to read the pin */
-#define FREQUENCY 5000
-
-/* time server settings */
-#define NTP_URL "pool.ntp.org"
-#define TZ_STRING "CET-1CDT,M3.5.0,M10.5.0/3"
diff --git a/source/nodemcu/statusclient/credentials.template b/source/nodemcu/statusclient/credentials.template
deleted file mode 100644
index 2ba711d..0000000
--- a/source/nodemcu/statusclient/credentials.template
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * file: credentials.template
- * desc: This file is part of the Krautspace Doorstatus project. It contains
- * wifi ssid and passwords for the statusclient.ino programm, that runs on a
- * NodeMCU with a ESP8266 chip.
- *
- * Rename this file into 'credentials.h' and adapt the values to your
- * wifi conditions.
- */
-
-/* wifi credentials */
-#define SSID_1 "your_first__wlan_ssid"
-#define PSK_1 "your_first_wlan_passwort"
-#define SSID_2 "your_second_wlan_ssid"
-#define PSK_2 "your_seconde_wlan_password"
-#define SSID_3 "your_third_wlan_ssid"
-#define PSK_3 "your_third_wlan_password"
diff --git a/source/nodemcu/statusclient/statusclient.ino b/source/nodemcu/statusclient/statusclient.ino
deleted file mode 100644
index 5faaa2e..0000000
--- a/source/nodemcu/statusclient/statusclient.ino
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * file: statusclient.ino
- * desc: This file is part of the Krautspace Doorstatus project. It's the
- * main file for a client, who deals with the input from a reed sensor and
- * push these values to a server. The code is make to run on a NodeMCU with
- * ESP8266 chip.
- */
-
-#include
-#include
-#include
-
-#include "config.h"
-#include "certs.h"
-#include "credentials.h"
-
-const int LED_PIN = 16; // D0
-const int REED_PIN = 5; // D1
-
-typedef enum {
- DOOR_CLOSED = 0,
- DOOR_OPEN = 1
-} door_state;
-door_state current_door_state = DOOR_CLOSED;
-
-BearSSL::WiFiClientSecure client;
-
-void init_serial() {
- /*
- * set baudrate and debug modus
- */
- Serial.begin(BAUD_RATE);
- Serial.setDebugOutput(DEBUG);
- Serial.println();
- Serial.println("[Srl] Serial interface initialized");
-}
-
-void init_pins() {
- /*
- * set gpio for reed sensor and led
- */
- pinMode(REED_PIN, INPUT_PULLUP);
- pinMode(LED_PIN, OUTPUT);
- digitalWrite(LED_PIN, HIGH);
- Serial.println("[Pin] LED and REED initialized");
-}
-
-void init_wifi() {
- /*
- * Creates the ssl context. Turns wifi off and than into
- * access point mode.
- * TODO: is 'turn of' needed!
- */
- ESP8266WiFiMulti wifi;
- WiFi.mode(WIFI_OFF);
- WiFi.mode(WIFI_STA);
- wifi.addAP(SSID_1, PSK_1);
- wifi.addAP(SSID_2, NULL);
- Serial.println("[Wifi] Wifi initialized");
- wifi.run();
- if (WiFi.status() == WL_CONNECTED) {
- Serial.print("[Wif] Connected to ");
- Serial.println(WiFi.SSID());
- Serial.print("[Wifi] IP: ");
- Serial.println(WiFi.localIP());
- set_clock();
- } else {
- Serial.println("[Wifi] Error: Failed to connect");
- signal_wifi_failed();
- }
-}
-
-door_state read_door_state() {
- /*
- * die initialisierung des reed-pin mit pullup bewirkt, daß am pin
- * 3,3 volt anliegen. die verbindung des pins mit GND sorgt dafür,
- * daß die spannung "abfließen" kann. dadurch hat der pin dann den
- * status 'low'.
- * geschlossene tür -> reed geschlossen -> low
- * geöffnete tür -> reed offen -> high
- */
- if (digitalRead(REED_PIN) == HIGH) {
- return DOOR_OPEN;
- }
- return DOOR_CLOSED;
-}
-
-void signal_door_changed() {
- /*
- * LED signal, if door is opened
- */
- uint8_t count = 2;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(100);
- digitalWrite(LED_PIN, HIGH);
- delay(100);
- }
-}
-
-void signal_send_successful() {
- /*
- * LED signal, if new status was send successful
- */
- uint8_t count = 5;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(100);
- digitalWrite(LED_PIN, HIGH);
- delay(100);
- }
-}
-
-void signal_clock_failed() {
- /*
- * LED signal, if time setting failed
- */
- uint8_t count = 2;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(500);
- digitalWrite(LED_PIN, HIGH);
- delay(500);
- }
- delay(2000);
-}
-
-void signal_wifi_failed() {
- /*
- * LED signal, if wifi initialication was failed
- */
- uint8_t count = 3;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(500);
- digitalWrite(LED_PIN, HIGH);
- delay(500);
- }
- delay(2000);
-}
-
-void signal_connect_failed() {
- /*
- * LED signal, if door is opened
- */
- uint8_t count = 4;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(500);
- digitalWrite(LED_PIN, HIGH);
- delay(500);
- }
- delay(2000);
-}
-
-void signal_send_failed() {
- /*
- * LED signal, if door is opened
- */
- uint8_t count = 5;
- for(uint8_t i=0; i!= count; ++i) {
- digitalWrite(LED_PIN, LOW);
- delay(500);
- digitalWrite(LED_PIN, HIGH);
- delay(500);
- }
- delay(2000);
-}
-
-void set_clock() {
- /*
- * We need time for certificate authorization
- */
- configTime(TZ_STRING, NTP_URL);
-
- Serial.print("[Clock] Waiting for NTP time sync");
- time_t now = time(nullptr);
- while (now < 8 * 3600 * 2) {
- delay(500);
- Serial.print(".");
- now = time(nullptr);
- }
- Serial.println("");
- struct tm timeinfo;
- gmtime_r(&now, &timeinfo);
- Serial.print("[Clock] Current time: ");
- Serial.println(asctime(&timeinfo));
-}
-
-int send_status(door_state state) {
-
- /*
- * Inits wifi (if needed) and send the status
- */
- char status[2] = "";
-
- if (state == DOOR_CLOSED) {
- strncpy(status, "0", 1);
- } else if (state == DOOR_OPEN) {
- strncpy(status, "1", 1);
- } else {
- return 1;
- }
-
- BearSSL::X509List server_cert(SERVER_CERT);
- BearSSL::X509List client_cert(CLIENT_CERT);
- BearSSL::PrivateKey client_key(CLIENT_KEY);
- client.setTrustAnchors(&server_cert);
- client.setClientRSACert(&client_cert, &client_key);
- Serial.println("[Ctx] SSL Context initialized");
- Serial.printf("[Send] Connect to %s:%i\n", SERVER_URL, SERVER_PORT);
- client.connect(SERVER_URL, SERVER_PORT);
- if (!client.connected()) {
- Serial.println("[Send] Can't connect to server");
- Serial.print("[Send] SSL Error: ");
- Serial.println(client.getLastSSLError());
- signal_send_failed();
- client.stop();
- return 1;
- } else {
- ESP.resetFreeContStack();
- uint32_t freeStackStart = ESP.getFreeContStack();
- Serial.println("[Send] Connection successful established");
- Serial.printf("[Send] Send status: %s\n", status);
- client.write(status);
- signal_send_successful();
- client.stop();
- }
- return 0;
-}
-
-
-void setup() {
-
- /*
- * things to do once at boot time
- */
- init_serial();
- init_pins();
- init_wifi();
-}
-
-void loop() {
-
- /*
- * things are running in a endless loop
- */
- if (WiFi.status() != WL_CONNECTED) {
- init_wifi();
- }
- door_state new_door_state = read_door_state();
- if (new_door_state != current_door_state) {
- Serial.printf("[Loop] Status has changed to %i\n", new_door_state);
- signal_door_changed();
- if (send_status(new_door_state) == 0) {
- current_door_state = new_door_state;
- }
- }
- delay(FREQUENCY);
-}
diff --git a/source/server/apistatusd.conf b/source/server/apistatusd.conf
index 601204d..73433e8 100644
--- a/source/server/apistatusd.conf
+++ b/source/server/apistatusd.conf
@@ -14,20 +14,12 @@ loglevel = debug
[server]
host = localhost
port = 10001
-cert = ./certs/statusd-pub.pem
-key = ./certs/statusd-key.pem
+cert = ./certs/server-pub.pem
+key = ./certs/server-key.pem
[client]
-cert = ./certs/statusclient-pub.pem
-# possible values: true, false, may
-required = true
+cert = ./certs/client-pub.pem
[api]
api = ./api
template = ./api_template
-
-[mastodon]
-send = false
-host = localhost
-token = aaaaa-bbbbb-ccccc-ddddd-eeeee
-
diff --git a/source/server/apistatusd.py b/source/server/apistatusd.py
index 9a89b4a..eff1010 100755
--- a/source/server/apistatusd.py
+++ b/source/server/apistatusd.py
@@ -1,26 +1,21 @@
#!/usr/bin/python3
-# file: apistatusd.py
+# file: statusd.py
# date: 26.07.2019
-# mail: berhsi@web.de
+# email: berhsi@web.de
# Status server, listening for door status updates. The IPv4 address and port
# to listen on are configurable, by default localhost:10001 is used. The
# connection is secured by TLS and client side authentication.
-try:
- import json
- import logging
- import os
- import socket
- import ssl
- import sys
- import requests
- import threading
- from time import time, localtime, strftime, sleep
- import configparser
-except ImportException as e:
- print('Import error: {}'.format(e))
+import json
+import logging
+import os
+import socket
+import ssl
+import sys
+from time import time, sleep
+import configparser
def certs_readable(config):
@@ -37,6 +32,7 @@ def certs_readable(config):
return False
return True
+
def print_config(config):
'''
Logs the config with level debug.
@@ -45,40 +41,8 @@ def print_config(config):
for section in config.sections():
logging.debug('Section {}'.format(section))
for i in config[section]:
- if i == 'token':
- logging.debug(' {}: {}'.format(i, 'aaaaa-bbbbb-ccccc-ddddd-eeeee'))
- else:
- logging.debug(' {}: {}'.format(i, config[section][i]))
+ logging.debug(' {}: {}'.format(i, config[section][i]))
-def create_ssl_context(config):
- '''
- Creates the ssl context.
- return: context object or None
- '''
- context = None
- requirement = None
- required = config['client']['required'].lower()
- if required == 'false':
- requirement = ssl.CERT_NONE
- elif required == 'may':
- requirement = ssl.CERT_OPTIONAL
- else: requirement = ssl.CERT_REQUIRED
- try:
- context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
- context.verify_mode = requirement
- context.load_cert_chain(certfile=config['server']['cert'],
- keyfile=config['server']['key'])
- context.load_verify_locations(cafile=config['client']['cert'])
- # ensure, compression is disabled (disabled by default anyway at the moment)
- context.options |= ssl.OP_NO_COMPRESSION
- context.options = ssl.PROTOCOL_TLS_SERVER
- context.options = ssl.OP_CIPHER_SERVER_PREFERENCE
- logging.debug('SSL context created')
- except Exception as e:
- logging.error('Failed to create SSL context')
- logging.error('Error: {}'.format(e))
- return None
- return context
def print_ciphers(cipherlist):
'''
@@ -86,58 +50,77 @@ def print_ciphers(cipherlist):
param1: dictionary
return: boolean
'''
- logging.debug('Available ciphers')
+ print('Available ciphers')
for i in cipherlist:
+ print('\n')
for j in i.keys():
- if j in ('name', 'protocol'):
- logging.debug('{}: {}'.format(j, i[j]))
+ print('{}: {}'.format(j, i[j]))
+ print('\n')
-def print_context(ctx):
- '''
- Prints the ssl settings for the given ssl context.
- param1: context object
- '''
- logging.debug('----------- context ----------------')
- logging.debug('Minimum version ssl: {}'.format(ctx.minimum_version))
- logging.debug('Maximum version ssl: {}'.format(ctx.maximum_version))
- logging.debug('SSL options enabled: {}'.format(ctx.options))
- logging.debug('Protocol: {}'.format(ctx.protocol))
- logging.debug('Verify flags certificates: {}'.format(ctx.verify_flags))
- logging.debug('Verify mode: {}'.format(ctx.verify_mode))
- print_ciphers(ctx.get_ciphers())
- logging.debug('------------------------------------')
def display_peercert(cert):
'''
Displays the values of a given certificate.
- param1: dictionary or none
+ param1: dictionary
+ return: boolean
'''
- if cert == None:
- logging.debug('Peer does not offer a certificate')
- elif len(cert) == 0:
- logging.debug('Peer certificate was not valid')
- else:
- logging.debug('Peer certificate commonName: {}'.format(
- cert['subject'][5][0][1]))
- logging.debug('Peer certificate serialNumber: {}'.format(
- cert['serialNumber']))
- logging.debug('Peer certificate notBefore: {}'.format(
- cert['notBefore']))
- logging.debug('Peer certificate notAfter: {}'.format(
- cert['notAfter']))
+ for i in cert.keys():
+ print('{}:'.format(i))
+ if i in ('subject', 'issuer'):
+ for j in cert[i]:
+ print('\t{}'.format(j))
+ else:
+ print('\t{}'.format(cert[i]))
+
def receive_buffer_is_valid(raw_data):
'''
Checks validity of the received buffer contents.
- param 1: byte object
+ param 1: byte
return: boolean
'''
- if raw_data.decode('utf-8', 'strict') in ('0', '1'):
+ if raw_data in (b'\x00', b'\x01'):
logging.debug('Argument is valid: {}'.format(raw_data))
return True
+
logging.debug('Argument is not valid: {}'.format(raw_data))
return False
+
+def change_status(raw_data, api):
+ '''
+ Write the new status together with a timestamp into the Space API JSON.
+ param 1: byte
+ param 2: string
+ return: boolean
+ '''
+
+ logging.debug('Change status API')
+ # todo: use walrus operator := when migrating to python >= 3.8
+ data = read_api(api)
+ if data is False:
+ return False
+
+ status, timestamp = set_values(raw_data)
+ if os.access(api, os.W_OK):
+ logging.debug('API file is writable')
+ with open(api, 'w') as api_file:
+ logging.debug('API file open successfull')
+ data["state"]["open"] = status
+ data["state"]["lastchange"] = timestamp
+ try:
+ json.dump(data, api_file, indent=4)
+ except Exception as e:
+ logging.error('Failed to change API file')
+ logging.error('{}'.format(e))
+ logging.debug('API file changed')
+ else:
+ logging.error('API file is not writable. Wrong permissions?')
+ return False
+ logging.info('Status successfull changed to {}'.format(status))
+ return True
+
+
def read_api(api):
'''
Reads the Space API JSON into a dict. Returns the dict on success and
@@ -147,6 +130,7 @@ def read_api(api):
return: dict or boolean
'''
logging.debug('Open API file: {}'.format(api))
+
# return early if the API JSON cannot be read
if not os.access(api, os.R_OK):
logging.error('Failed to read API file')
@@ -154,138 +138,30 @@ def read_api(api):
logging.debug('API is readable')
with open(api, 'r') as api_file:
- logging.debug('API file successfully readable opened')
+ logging.debug('API file successfully opened')
try:
api_json_data = json.load(api_file)
- logging.debug('API file successfully read')
+ logging.debug('API file read successfull')
except Exception as e:
logging.error('Failed to read API file: {}'.format(e))
return False
return api_json_data
-def change_status(status, timestamp, filename):
- '''
- Write the new status together with a timestamp into the Space API JSON.
- param 1: byte object
- param 2: string
- return: boolean
- '''
- logging.debug('Change status API')
- # todo: use walrus operator := when migrating to python >= 3.8
- data = read_api(filename)
- if data is False:
- return False
- if os.access(filename, os.W_OK):
- logging.debug('API file is writable')
- with open(filename, 'w') as api_file:
- logging.debug('API file successfull writable opened')
- data["state"]["open"] = status
- data["state"]["lastchange"] = timestamp
- try:
- json.dump(data, api_file, indent=4)
- except Exception as e:
- logging.error('Failed to change API file')
- logging.error('{}'.format(e))
- return False
- logging.debug('API file changed')
- else:
- logging.error('API file is not writable. Wrong permissions?')
- return False
- logging.info('API file successfull changed to {}'.format(status))
- return True
-
-def get_status_and_time(raw_data):
+def set_values(raw_data):
'''
Create a timestamp, changes the value of the given byte into a string
and returns both.
- param 1: byte object
- return: tuple (boolean, integer)
+ param 1: byte
+ return: tuple
'''
- status = True if raw_data.decode('utf-8', 'strict') == '1' else False
+ status = True if raw_data == b'\x01' else False
timestamp = int(str(time()).split('.')[0])
logging.debug('Set values for timestamp: {} and status: {}'.format(
str(timestamp), str(status)))
return (status, timestamp)
-def join_path(host, api):
- '''
- Becomes two parts (host and api) of the mastodon url and concanate them
- param1: string
- param2: string
- return: string
- '''
- url = ''
- try:
- if host[-1] == '/' and api[0] == '/':
- url = ''.join((host, api[1:]))
- elif host[-1] != '/' and api[0] != '/':
- url = '/'.join((host, api))
- else:
- url = ''.join((host, api))
- except TypeError as e:
- logging.error('Can´t join path: {}'.format(e))
- return url
-
-class Toot(threading.Thread):
- '''
- The thread to toot the status to mastodon.
- '''
- def __init__(self, status, timestamp, config):
- '''
- param1: boolean
- param2: integer
- param3: dictionary
- '''
- threading.Thread.__init__(self)
- self.status = status
- self.config = config
- self.timestamp = timestamp
- self.api = '/api/v1/statuses'
- self.auth = {'Authorization': ''}
- self.data = {'status': ''}
- self.url = ''
-
- def run(self):
- '''
- return: boolean
- '''
- timeformat = '%d.%m.%Y %H:%M Uhr'
- # check if status is valid
- if self.status not in (True, False):
- logging.error('Invalid status to toot')
- return False
- # convert seconds into timestring
- try:
- timestring = strftime(timeformat, localtime(self.timestamp))
- except Exception as e:
- logging.error('Can not convert timestamp into timestring')
- return False
- # set status message
- if self.status == True:
- self.data['status'] = 'Krautspace is open since: {}'.format(timestring)
- elif self.status == False:
- self.data['status'] = 'Krautspace is closed since: {}'.format(timestring)
- logging.debug('Message: {}'.format(self.data['status']))
- # build mastodon api url
- self.url = join_path(self.config['mastodon']['host'], self.api)
- # build authentcation header
- self.auth['Authorization'] = 'Bearer {}'.format(
- self.config['mastodon']['token'])
- # and finaly send request to mastodon
- try:
- logging.debug('Try to toot status')
- response = requests.post(self.url, data = self.data,
- headers = self.auth)
- if response.status_code == 200:
- logging.info('Toot successful send')
- return True
- else:
- logging.error('Failed to toot. Response: {}'.format(response.status_code))
- except Exception as e:
- logging.error('Exception occurred: {}'.format(e))
- return False
def main():
'''
@@ -296,15 +172,15 @@ def main():
OP_DONT_ISERT_EMPTY_FRAGMENTS: prevention agains CBC 4 attack
(cve-2011-3389)
'''
- answer = '3'.encode(encoding='utf-8', errors='strict')
- loglevel = logging.INFO
+
+ loglevel = logging.WARNING
formatstring = '%(asctime)s: %(levelname)s: %(message)s'
logging.basicConfig(format=formatstring, level=loglevel)
default_config = {
'general': {
'timeout': 3.0,
- 'loglevel': 'info'
+ 'loglevel': 'warning'
},
'server': {
'host': 'localhost',
@@ -313,21 +189,14 @@ def main():
'key': './certs/server.key'
},
'client': {
- 'cert': './certs/client.crt',
- 'required': 'true'
+ 'cert': './certs/client.crt'
},
'api': {
'api': './api',
'template': './api_template'
- },
- 'mastodon': {
- 'send': 'false',
- 'host': 'localhost',
- 'token': 'aaaaa-bbbbb-ccccc-ddddd-eeeee'
}
}
- logging.info('Try to read config file')
- configfile = './apistatusd.conf'
+ configfile = './statusd.conf'
config = configparser.ConfigParser()
config.read_dict(default_config)
if not config.read(configfile):
@@ -341,7 +210,6 @@ def main():
default_config['general']['loglevel'])
config.set('general', 'loglevel', default_config['general']['loglevel'])
- logging.info('Set loglevel to {}'.format(config['general']['loglevel'].upper()))
logger.setLevel(config['general']['loglevel'].upper())
print_config(config)
@@ -351,75 +219,88 @@ def main():
logging.error('Cert check failed\nExit')
sys.exit(1)
- # ssl context erstellen
- context = create_ssl_context(config)
- if context is not None:
- print_context(context)
- else: sys.exit(2)
+ context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+ context.verify_mode = ssl.CERT_REQUIRED
+ context.load_cert_chain(certfile=config['server']['cert'],
+ keyfile=config['server']['key'])
+ context.load_verify_locations(cafile=config['client']['cert'])
+ context.set_ciphers('EECDH+AESGCM') # only ciphers for tls 1.2 and 1.3
+ context.options = ssl.OP_CIPHER_SERVER_PREFERENCE
+ # ensure, compression is disabled (disabled by default anyway at the moment)
+ context.options |= ssl.OP_NO_COMPRESSION
+ logging.debug('SSL context created')
+ # print_ciphers(context.get_ciphers())
- try:
- # tcp socket öffnen => MySocket
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as MySocket:
- logging.debug('TCP Socket created')
- MySocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- MySocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
- keep = MySocket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE)
- logging.debug('Socket keepalive: {}'.format(keep))
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as mySocket:
+ logging.debug('Socket created')
+ mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
+ keep = mySocket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE)
+ logging.debug('Socket keepalive: {}'.format(keep))
+ try:
+ mySocket.bind((config['server']['host'], int(config['server']['port'])))
+ mySocket.listen(5)
+ except Exception as e:
+ logging.error('Unable to bind and listen')
+ logging.error('{}'.format(e))
+ sys.exit(1)
+ logging.info('Listening on {} at Port {}'.format(config['server']['host'],
+ config['server']['port']))
+
+ while True:
try:
- MySocket.bind((config['server']['host'], int(config['server']['port'])))
- MySocket.listen(5)
- logging.info('Listening on {} at Port {}'.format(config['server']['host'],
- config['server']['port']))
- except Exception as e:
- logging.error('Unable to bind and listen')
- logging.error('{}'.format(e))
- sys.exit(3)
- # endlos auf verbindungen warten => ClientSocket
- while True:
- ClientSocket, ClientAddress = MySocket.accept()
- logging.info('Client connected: {}:{}'.format(ClientAddress[0], ClientAddress[1]))
- # die verbindung in den ssl-context verpacken => Connection
+ fromSocket, fromAddr = mySocket.accept()
+ logging.info('Client connected: {}:{}'.format(fromAddr[0], fromAddr[1]))
try:
- Connection = context.wrap_socket(ClientSocket, server_side=True)
- logging.info('SSL Connection established')
- Connection.settimeout(float(config['general']['timeout']))
- logging.debug('Connection timeout set to {}'.format(config['general']['timeout']))
- cert = Connection.getpeercert(binary_form=False)
- display_peercert(cert)
+ fromSocket.settimeout(float(config['general']['timeout']))
+ logging.debug('Connection timeout set to {}'.format(
+ config['general']['timeout']))
+ except Exception:
+ logging.error('Cannot set timeout to {}'.format(
+ config['general']['timeout']))
+ try:
+ conn = context.wrap_socket(fromSocket, server_side=True)
+ conn.settimeout(float(config['general']['timeout']))
+ except socket.timeout:
+ logging.error('Socket timeout')
except Exception as e:
- logging.error('Unexpected error: {}'.format(e))
- continue
- # empfangen und antworten
- raw_data = Connection.recv(1)
+ logging.error('Connection failed: {}'.format(e))
+ logging.info('Connection established')
+ logging.info('Peer certificate commonName: {}'.format(
+ conn.getpeercert()['subject'][5][0][1]))
+ logging.debug('Peer certificate serialNumber: {}'.format(
+ conn.getpeercert()['serialNumber']))
+
+ raw_data = conn.recv(1)
if receive_buffer_is_valid(raw_data) is True:
- status, timestamp = get_status_and_time(raw_data)
- if change_status(status, timestamp, config['api']['api']) is True:
- answer = raw_data
- if config['mastodon']['send'].lower() == 'true':
- logging.debug('Toot is set to true')
- try:
- toot_thread = Toot(status, timestamp, config)
- toot_thread.run()
- except InitException as e:
- logging.error('InitException: {}'.format(e))
- except Exception as ex:
- logging.debug('Exception: {}'.format(ex))
- else: logging.debug('Toot is set to false')
- logging.debug('Send {} back'.format(raw_data))
- Connection.send(answer)
- Connection.close()
- except KeyboardInterrupt:
- logging.info('Keyboard interrupt received')
- if MySocket:
- MySocket.close()
- logging.debug('TCP socket closed')
- sys.exit(255)
- except Exception as e:
- logging.error('{}'.format(e))
- if MySocket:
- MySocket.close()
- logging.debug('TCP socket closed')
- sys.exit(254)
+ if change_status(raw_data, config['api']['api']) is True:
+ logging.debug('Send {} back'.format(raw_data))
+ conn.send(raw_data)
+ # change_status returns false:
+ else:
+ logging.info('Failed to change status')
+ if conn:
+ conn.send(b'\x03')
+ # receive_handle returns false:
+ else:
+ logging.info('Invalid argument received: {}'.format(raw_data))
+ logging.debug('Send {} back'.format(b'\x03'))
+ if conn:
+ conn.send(b'\x03')
+ sleep(0.1) # protection against dos
+ except KeyboardInterrupt:
+ logging.info('Keyboard interrupt received')
+ sys.exit(1)
+ except Exception as e:
+ logging.error('{}'.format(e))
+ continue
+ finally:
+ if mySocket:
+ logging.info('Shutdown socket')
+ try:
+ mySocket.shutdown(socket.SHUT_RDWR)
+ except Exception as e:
+ logging.error(f'Error while shutdown socket: {e}')
+ return 0
if __name__ == '__main__':
diff --git a/source/server/apistatusd.service b/source/server/apistatusd.service
index 33111bc..a76f4dc 100644
--- a/source/server/apistatusd.service
+++ b/source/server/apistatusd.service
@@ -4,7 +4,6 @@ After=systemd-network.service network.target
[Service]
Type=simple
-Restart=on-failure
WorkingDirectory=/opt/doorstatus/
ExecStart=/opt/doorstatus/apistatusd.py
SyslogIdentifier=doorstatus
diff --git a/source/server/setstatus.py b/source/server/setstatus.py
index e6fccfa..f62a2e3 100755
--- a/source/server/setstatus.py
+++ b/source/server/setstatus.py
@@ -9,7 +9,8 @@
# krautspaces doorstatus.
# client, who connects to the statusserver at port 10001 to update the
-# krautspace door status.
+# krautspace door status. If no status is given as argument, he reads from
+# stdin until input is 0 or 1.
import os
import ssl
@@ -57,19 +58,16 @@ class SetStatus:
def check_status(self):
"""
- checkes, if the self.status variable is a valid value
return: boolean
"""
- if self.status in ('0', '1'):
- self.log.debug('Set status to {}'.format(self.status))
+ if self.status in (0, 1):
+ self.log.debug('Set value to {}'.format(self.status))
+ self.status = bytes([self.status])
return True
- self.log.debug('{} is not a valid status'.format(self.status))
return False
def set_config(self):
"""
- Tries to read and use the values from the configuration file. If
- this failes, we still use the default values.
"""
self.log = logging.getLogger()
# read config file
@@ -91,8 +89,7 @@ class SetStatus:
def check_certs(self, certs):
"""
- Check if certs are readable.
- return: boolean
+ Check if certs readable.
"""
self.log.debug('Check certificates')
for certfile in certs:
@@ -114,28 +111,25 @@ class SetStatus:
def create_ssl_context(self):
"""
- Creates SSL context
- return: context object or false
"""
- try:
- context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
- except Exception as e:
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH,
+ cafile=self.config['server']['cert'])
+ if not context:
self.log.error('Failed to create SSL Context')
return False
- context.load_verify_locations(cafile=self.config['server']['cert'])
- context.load_cert_chain(certfile=self.config['client']['cert'],
- keyfile=self.config['client']['key'])
context.set_ciphers('EECDH+AESGCM') # only ciphers for tls 1.2 and 1.3
context.options |= getattr(ssl._ssl, 'OP_NO_COMPRESSION', 0)
+ try:
+ context.load_cert_chain(certfile=self.config['client']['cert'],
+ keyfile=self.config['client']['key'])
+ except Exception as e:
+ self.log.error('Failed to load cert chain')
+ return False;
self.log.debug('SSL context created')
return context
def create_ssl_socket(self, config, context):
"""
- Opens a socket and wrapes the socket into the given ssl context.
- param1: dictionary
- param2: ssl context
- return: ssl-socket or false
"""
bare_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
if not bare_socket:
@@ -198,6 +192,7 @@ class SetStatus:
# check given status
if self.check_status() is False:
+ self.log.error('No valid status given')
exit(1)
# log config if level is debug
@@ -216,20 +211,18 @@ class SetStatus:
if self.context is False:
exit(3)
- # get a ssl encrypted connection
+ # get connection
self.connection = self.create_ssl_connection()
# send status
try:
self.log.debug('Send new status: {}'.format(self.status))
- self.connection.send(self.status.encode(encoding='utf-8',
- errors='strict'))
+ self.connection.send(self.status)
except Exception as e:
self.log.error('Error: {}'.format(e))
exit(6)
try:
- response = self.connection.recv(1).decode(encoding='utf-8',
- errors='strict')
+ response = self.connection.recv(1)
self.log.debug('Server returns: {}'.format(response))
if response == self.status:
self.log.info('Status sucessfull updated')
@@ -240,12 +233,3 @@ class SetStatus:
self.log.error('Error: {}'.format(e))
exit(7)
-
-if __name__ == '__main__':
- s = SetStatus()
- if len(argv) < 2:
- log.error('Usage: setstatus.py <0|1>')
- exit(255)
- else:
- s.run(argv[1])
-