Mit dem Siegeszug der Pushnachrichten war es nicht mehr notwendig seine email zu Pollen (in gleichen Abständen prüfen). Wie eine SMS oder ein Anruf wird der Empfänger benachrichtig, wenn etwas neues eingetroffen ist.

Diese neue Technik sollte doch auch ein Briefkasten beherschen können?

Worauf basiert das Projekt?

  • Arduino Pro Mini (3.3v 8Mhz)
  • Reed Kontakt
  • NRF24LF01+
  • TP4056 Laderegler
  • 700mAh lipo
  • 5W Solar Panel
  • DHT22 - Temperatur und Luftfeuchte (optional)
  • BMP180 - Temperatur und Luftdruck (optional)
  • MySensors integration
  • Telegram Push service

Dieser Sensor war einer meiner ersten. Vor kurzem habe ich diesen komplett überarbeitet. Er bestand vorher z.B. noch aus einem Arduino Nano. Der hat aber jedoch viel zu viel Strom gezogen. Mit 2 AA Batterien hat er nichtmal eine Woche durchgehalten. Daraufhin habe ich die Batterien durch einen LiPo und ein Solar-Panel ersetzt. Nun hat er schon deutlich besser gehalten. Jedoch war er immer noch sehr fehlerhaft. Als ich den neuen Sensor mit dem PCB gelötet habe, ist mir aufgefallen, dass ein PIN am Laderegler nicht ganz fest war. Mit etwas Hitze vom Lötkolben wäre das Ding wohl wieder einsatzbereit. Aber ich habe ihn dennoch komplett neu erstellt.

Hardware

IMG_20180112_144758
Als Basis für meine Sensoren nehme ich das PCB Layout von Sunberg84 (https://www.mysensors.org/hardware/newbie-pcb)
Besonders den NRF Chip zu löten, macht das PCB sehr einfach. Über Mysensors /Openhardware.io kann man es sehr günstig kaufen. Natürlich kann man auch alles selbst löten.

fritzing_briefkasten
Das Schema ist nicht 100% akkurat, aber sollte eine Idee geben, wie man die Elemente anklemmt und verlötet.
Das Solar Panel wird am Eingang vom TP4056 angeschlossen. Hier wird dann noch die Batterie und die Versorgung angeschlossen. Da der Arduino nur 3.3V verträgt, wird noch ein AM1117 vorgeschaltet. Die Batterie könnte theoretisch auch unter 3.3V fallen, was aber weder für Arduino noch für den NRF Chip ein Problem sein sollte. Der NRF steigt erst bei unter 1.9V aus. Zur Überwachung werden die Spannung der Batterie und des Solar Panels gemessen. Die Messung geht über einen Spannungsteiler. Intern wird mit der 1.1V Referenz-Spannung gemessen. Normal misst man gegen VCC vom Arduino. Wenn die nicht 100% passt, müsste man dagegen kompensieren.
Wichtig sind die Kondesatoren um die Spannung am Ausgang des Spannungswandler zu glätten. Besonders der NRF Chip ist anfällig für ein Rauschen auf der Spannungsversorgung.

Sensoren

Um die Deckelöffnung zu erkennen brauchen wir nur den Reed Kontakt Sensor. Da der Sensor nun mal draußen ist, habe ich noch Sensoren für Temperatur / Luftfeuchte und Luftdruck angeschlossen.

Deckelsensor

Das ist ein einfacher Reed Kontakt. In dem einem Teil ist ein Schalter und in dem anderen ein Magnet der den Schalter schließt und öffnet, wenn der Magnet nah genug kommt bzw. entfernt wird.

  • PIN: 3 - INPUT_PULLUP

reed_kontakt

DHT22

Dieser Sensor kann Temperatur und die relative Luftfeuchtigkeit messen. Ausgelesen wird er über einen PIN + Spannungsversorgung. Zum lesen gibt es eine open source Bibliothek

  • PIN: 5 - Daten PIN

BMP180

Der BMP180 von Bosch ist echt ein erstaunlicher Sensor. Auf der größe ist er sehr genau und verbraucht sehr wenig Strom. Auch hier gibt es eine open source Bibliothek. Die Daten können über I2C gelesen werden.

  • PIN: A4 (SDA)
  • PIN: A5 (SCL)

Batterie

Als Batterie habe ich einen LiPo mit 700mAh Kapazität genutzt. Dieser hat sogar eine Schutzschaltung gegen Über- und Unterspannung. Das TP4056 Board hat auch eine integrierte Schutzschaltung. Besser zwei als gar keine.

  • PIN: A0

Die Spannung wird über einen Spannungsteiler gemessen. Dieser ist so gewählt, dass maximal 1,1V an A0 anliegt. Wenn 5,78V an VCC anliegen, sind es an A0 1,1V.
Spannungsteiler

Solar Panel

Das Solar Panel speist über den TP4056 die Batterie. Um einen Einblick in die Sonneneinstrahlung zu bekommen soll auch die Spannung gemessen werden. Auf eine Leistungsmessung verzichte ich, da der Arduino dann öfter und länger wach sein müsste. Die Spannung wird wie an der Batterie gemessen.

  • PIN: A1

Bilder

Als Box benutze ich Aufputzdosen zum Verlegen von Kabeln. In die 85x85mm Dosen passen die Bauteile gut rein.
20160324_091210
20160324_091221
20160324_091233

Programmierung - Arduino

Ich habe das Ganze Projekt in mehrere Dateien aufgeteilt. Pro Sensor meistens eine Datei. Wer den Code direkt nutzen möchte muss schon ein MySensor Netzwerk am laufen haben.

Struktur

Sensor_Briefkasten_2017.ino
|-- battery.ino
|-- deckel.ino
|-- dht.ino
|-- Druck.ino
|-- solar.ino

Abhänigkeiten

Die Bibliotheken kann man alle über die Arduino IDE beziehen. Bei der Mysensors Bibliothek würde ich aber empfehlen, dass direkt von deren Seite zu laden. mysensors.org

#include <MySensors.h> //Bibliothek für Funknetzwerk - http://mysensors.org
#include <SPI.h>       //Bibliothek für Kommunikation über SPI zum BMP180
#include <DHT.h>       //Bibliothek für DHT22 Sensor
#include <SFE_BMP180.h>//Bibliothek für BMP180 Sensor

Haupt-Datei

/*

Sensor: Briefkasten

Sensor Deckel: Reedkontakt
Sensor Temperatur / Luftfeuchte: DHT22
Sensor Temperatur / Druck: BMP180

Änderungen

28.12.2017 1.0.0 Erstellt

Author: Hermann Kaiser

*/

/*
	Mysensor
*/

//#define MY_DISABLED_SERIAL
//#define MY_DEBUG
#define MY_WAIT_TX 5
#define MY_RECONNECT 7500
#define MY_NODE_ID 101
#include <MySensors.h>
//=============================================================

/*
	Globale Einstellungen
*/
#include <SPI.h>
//#define SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define SENSOR_SLEEP_TIME 30000
#else
#define SENSOR_SLEEP_TIME 300000
#endif // SERIAL_DEBUG

uint8_t g_actRun = 5;
//=============================================================


/*
   Batterie
   LiPo 3,7V
*/

#define BATTERY_SENSE_PIN A0
float batteryV = 0;
uint8_t batteryPcnt = 0;
uint8_t lastBatteryPcnt = 0;
//=============================================================

/*
	Deckelsensor: Reed Kontakt
*/
#define DECKEL_REED_CHILD 0
#define DECKEL_PIN  3  // Arduino Digital I/O pin for button/reed switch
boolean deckel_ackReceived = 0;
boolean deckel_value;
boolean deckel_oldValue = 0;
boolean deckel_sendRunning = 0;
MyMessage deckel_reed_msg(DECKEL_REED_CHILD, V_TRIPPED);
//=============================================================


/*
	DHT22 Feuchtigkeitssensor
*/
#include <DHT.h>
#define DHT_HUM_CHILD 1
#define DHT_TEMP_CHILD 2
#define HUMIDITY_SENSOR_DIGITAL_PIN 5
DHT dht;
float last_dht_Temp = -1.0;
float last_dht_Hum = -1.0;
MyMessage dht_hum_msg(DHT_HUM_CHILD, V_HUM);
MyMessage dht_temp_msg(DHT_TEMP_CHILD, V_TEMP);
//=============================================================

/*
	Drucksensor BMP180
*/
#include <SFE_BMP180.h>
#define ALTITUDE 298.0
SFE_BMP180 pressure;
float druck_pressure;
float druck_temperature;
float last_druck_pressure = -1.0;
float last_druck_temperature = -1.0;

#define DRUCK_BARO_CHILD 3 
#define DRUCK_TEMP_CHILD 4

MyMessage druck_temp_msg(DRUCK_TEMP_CHILD, V_TEMP);
MyMessage druck_pressure_msg(DRUCK_BARO_CHILD, V_PRESSURE);
//=============================================================


/*
	Solarpanel: 5V
*/
#define SOLAR_SENSE_PIN A1
float solar_voltage = 0;
float last_solar_voltage = -1.0;

#define SOLAR_CHILD 5
MyMessage solar_voltage_msg(SOLAR_CHILD, V_VOLTAGE);

//=============================================================

void before()
{
	Serial.println(F("Node Before"));
	setupBattery();	
}

void presentation()
{
	Serial.println(F("Node Presentation"));
	// Send the Sketch Version Information
	sendSketchInfo("Briefkasten", "3.0");

	deckel_presentation();
	dht_presentation();
	druck_presentation();
	solar_presentation();

}

void setup()
{
	Serial.println(F("Node Setup"));
	deckel_setup();
	dht_setup();
	druck_setup();
	solar_setup();

}

void loop()
{

	// Temp/Luftf. nur alle 3 Aufrufe auslesen (15min)
	if (g_actRun >= 3)
	{
		// Temperatur und Luftfeuchtigkeit messen / senden
		dht_loop();

		// Druck und Temperatur messen / senden
		druck_loop();

		// Batterie Level lesen und versenden
		readBattery();

		// Solar 
		solar_loop();

		g_actRun = 0;
	}

	// Deckel immer prüfen
	deckel_loop();

#ifdef SERIAL_DEBUG
	Serial.print(F("Run: "));
	Serial.print(g_actRun);
	Serial.print(F(" - Briefkasten: "));
	Serial.println(deckel_value ? "offen" : "geschlossen");
	Serial.flush();
#endif // SERIAL_DEBUG


	// Verbindung prüfen und schlafen
	if (isTransportReady())
	{
		// 300s Schlafen, wenn return = 0, dann hat Timer geweckt, sonst der PIN-Interrupt
		// -1 von timer geweckt, 0..1 Interrupt-Nummer
		if (-1 == sleep(digitalPinToInterrupt(DECKEL_PIN),CHANGE, SENSOR_SLEEP_TIME, false))
			++g_actRun;
	}
	else
	{
		delay(100); // Kondensator aufladen
		wait(MY_RECONNECT);
		if (!isTransportReady()) // Zeit verdoppeln, wenn Verbindung immer noch nicht besteht
			wait(MY_RECONNECT);
	}

}

void receive(const MyMessage &msg)
{
#ifdef SERIAL_DEBUG
	Serial.println(F("msg received"));
	//Serial.println(msg.node-id);
#endif // SERIAL_DEBUG
	if (msg.isAck())
	{
		deckel_ackReceived = 1;
#ifdef SERIAL_DEBUG
		Serial.println(F("Ack received"));
#endif // SERIAL_DEBUG
	}
}

Batterie

void setupBattery()
{
	// use the 1.1 V internal reference
#if defined(__AVR_ATmega2560__)
	analogReference(INTERNAL1V1);
#else
	analogReference(INTERNAL);
#endif
}

void readBattery()
{
	// get the battery Voltage
	int sensorValue = analogRead(BATTERY_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(BATTERY_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(BATTERY_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(BATTERY_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(BATTERY_SENSE_PIN);
	sensorValue /= 5;
#ifdef SERIAL_DEBUG
	Serial.println(sensorValue);
#endif

	// 2M, 470k divider across battery and using internal ADC ref of 1.1V
	// Sense point is bypassed with 0.1 uF cap to reduce noise at that point
	// ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts
	// 5,78/1024 = Volts per bit = 0.003363075
	batteryV = sensorValue * 0.0056455078125;
	// 3,2v .. 4,2v
	batteryPcnt = map(sensorValue, 566, 743, 0, 100);

	if (abs(lastBatteryPcnt - batteryPcnt) > 1)
	{
		if (sendBatteryLevel(batteryPcnt, 0))
			lastBatteryPcnt = batteryPcnt;
	}
	

#ifdef SERIAL_DEBUG
	Serial.print(F("Battery Voltage: "));
	Serial.print(batteryV);
	Serial.println("V");

	Serial.print(F("Battery percent: "));
	Serial.print(batteryPcnt);
	Serial.println("%");
#endif

}

Deckel - Reed Kontakt


void deckel_presentation()
{
	pinMode(DECKEL_PIN, INPUT_PULLUP);
}

void deckel_setup()
{
	present(DECKEL_REED_CHILD, S_DOOR);
}


void deckel_loop()
{
	if (!deckel_sendRunning)
		deckel_value = digitalRead(DECKEL_PIN);
	if (deckel_value != deckel_oldValue || deckel_sendRunning) {
		// Send in the new value
		send(deckel_reed_msg.set(deckel_value == HIGH ? 1 : 0), 1);
		deckel_sendRunning = 1;
		long sendStart = millis();
		while (deckel_sendRunning && millis() - sendStart < 500)
		{
			wait(50, deckel_reed_msg.getCommand(), deckel_reed_msg.type); //Ein bisschen Wach für die Antwort bleiben
			if (deckel_ackReceived)
			{
				deckel_ackReceived = 0;
				deckel_sendRunning = 0;
				deckel_oldValue = deckel_value;
#ifdef SERIAL_DEBUG
					Serial.print(F("Ackdelay: "));
					Serial.println(millis() - sendStart);
#endif // SERIAL_DEBUG
			}
		}
	}
#ifdef SERIAL_DEBUG
		//Serial.println(++cnt);
		Serial.flush();
#endif // SERIAL_DEBUG

}

DHT22 - Temperatur und relative Luftfeuchte

void dht_presentation()
{
	// Register all sensors to gw (they will be created as child devices)
	present(DHT_HUM_CHILD, S_HUM);
	present(DHT_TEMP_CHILD, S_TEMP);
}

void dht_setup()
{
	dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
}

void dht_loop()
{
	delay(dht.getMinimumSamplingPeriod());

	float temperature = dht.getTemperature();
	if (isnan(temperature)) {
#ifdef SERIAL_DEBUG
			Serial.println(F("Failed reading temperature from DHT"));
#endif // SERIAL_DEBUG
	}
	else if (temperature != last_dht_Temp) {
		
		if(send(dht_temp_msg.set(temperature, 1)))
			last_dht_Temp = temperature;
		wait(MY_WAIT_TX);
#ifdef SERIAL_DEBUG
			Serial.print(F("T: "));
			Serial.println(temperature);
#endif // SERIAL_DEBUG
	}

	float humidity = dht.getHumidity();
	if (isnan(humidity)) {
#ifdef SERIAL_DEBUG
			Serial.println(F("Failed reading humidity from DHT"));
#endif // SERIAL_DEBUG
	}
	else if (humidity != last_dht_Hum) {		
		if(send(dht_hum_msg.set(humidity, 1)))
			last_dht_Hum = humidity;
		wait(MY_WAIT_TX);
#ifdef SERIAL_DEBUG
			Serial.print(F("H: "));
			Serial.println(humidity);
#endif // SERIAL_DEBUG
	}

}

BMP180 Drucksensor

void druck_presentation()
{
	// Send the sketch version information to the gateway and Controller
	//sendSketchInfo("Pressure Sensor", "2.1");
	// Register sensors to gw (they will be created as child devices)
	present(DRUCK_BARO_CHILD, S_BARO);
	present(DRUCK_TEMP_CHILD, S_TEMP);
}

void druck_setup()
{


	if (pressure.begin())
		Serial.println(F("BMP180 init success"));
	else
	{
		// Oops, something went wrong, this is usually a connection problem,
		// see the comments at the top of this sketch for the proper connections.
		Serial.println(F("BMP180 init fail\n\n"));
	}

}

void druck_loop()
{
	druck_getBmpReadings();

	// Druck versenden
	if (abs(druck_pressure - last_druck_pressure) > 0.1)
	{
#ifdef SERIAL_DEBUG
		Serial.print(F("Senden, Druck: "));
		Serial.print(druck_pressure, 1);
		Serial.println(F(" mb"));
#endif // SERIAL_DEBUG
		if (send(druck_pressure_msg.set(druck_pressure, 1), 0))
			last_druck_pressure = druck_pressure;
	}
	// Temperatur versenden
	if (abs(druck_temperature - last_druck_temperature) > 0.1)
	{
		if (send(druck_temp_msg.set(druck_temperature, 1), 0))
			last_druck_temperature = druck_temperature;
	}

}


double T, P, p0, a;
void druck_getBmpReadings()
{
	char status;
	status = pressure.startTemperature();
	if (status != 0)
	{
		// Wait for the measurement to complete:
		delay(status);

		// Retrieve the completed temperature measurement:
		// Note that the measurement is stored in the variable T.
		// Function returns 1 if successful, 0 if failure.

		status = pressure.getTemperature(T);
		if (status)
		{
			druck_temperature = (float)T;
			// Print out the measurement:
#ifdef SERIAL_DEBUG
			Serial.print(F("temperature: "));
			Serial.print(T, 2);
			Serial.println(F(" deg C"));
#endif // SERIAL_DEBUG

			// Start a pressure measurement:
			// The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
			// If request is successful, the number of ms to wait is returned.
			// If request is unsuccessful, 0 is returned.

			status = pressure.startPressure(3);
			if (status)
			{
				// Wait for the measurement to complete:
				delay(status);

				// Retrieve the completed pressure measurement:
				// Note that the measurement is stored in the variable P.
				// Note also that the function requires the previous temperature measurement (T).
				// (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
				// Function returns 1 if successful, 0 if failure.

				status = pressure.getPressure(P, T);
				if (status)
				{
					// Print out the measurement:
#ifdef SERIAL_DEBUG
					Serial.print(F("absolute pressure: "));
					Serial.print(P, 2);
					Serial.print(F(" mb, "));
					Serial.print(P * 0.0295333727, 2);
					Serial.println(F(" inHg"));
#endif // SERIAL_DEBUG

					// The pressure sensor returns abolute pressure, which varies with altitude.
					// To remove the effects of altitude, use the sealevel function and your current altitude.
					// This number is commonly used in weather reports.
					// Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
					// Result: p0 = sea-level compensated pressure in mb
					//druck_pressure = P;
					p0 = pressure.sealevel(P, ALTITUDE); // we're at 1655 meters (Boulder, CO)
					druck_pressure = (float)p0;
#ifdef SERIAL_DEBUG
					Serial.print(F("relative (sea-level) pressure: "));
					Serial.print(p0, 2);
					Serial.print(F(" mb, "));
					Serial.print(p0 * 0.0295333727, 2);
					Serial.println(F(" inHg"));
#endif // SERIAL_DEBUG

					// On the other hand, if you want to determine your altitude from the pressure reading,
					// use the altitude function along with a baseline pressure (sea-level or other).
					// Parameters: P = absolute pressure in mb, p0 = baseline pressure in mb.
					// Result: a = altitude in m.

					//a = pressure.altitude(P, p0);
#ifdef SERIAL_DEBUG
					//Serial.print(F("computed altitude: "));
					//Serial.print(a, 0);
					//Serial.println(F("m"));
#endif // SERIAL_DEBUG
				}
				else Serial.println(F("error retrieving pressure measurement\n"));
			}
			else Serial.println(F("error starting pressure measurement\n"));
		}
		else Serial.println(F("error retrieving temperature measurement\n"));
	}
	else Serial.println(F("error starting temperature measurement\n"));
}

Solar Panel

void solar_presentation()
{
	present(SOLAR_CHILD, S_MULTIMETER);
}


void solar_setup()
{

}

void solar_loop()
{
	// get the solar Voltage
	int sensorValue = analogRead(SOLAR_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(SOLAR_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(SOLAR_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(SOLAR_SENSE_PIN);
	delay(10);
	sensorValue += analogRead(SOLAR_SENSE_PIN);
	sensorValue /= 5;
#ifdef SERIAL_DEBUG
	Serial.println(sensorValue);
#endif

	// 2M, 470k divider across battery and using internal ADC ref of 1.1V
	solar_voltage = sensorValue * 0.0056455078125;
	if (abs(last_solar_voltage - solar_voltage) > 0.1)
	{
		if (send(solar_voltage_msg.set(solar_voltage, 1), 0))
			last_solar_voltage = solar_voltage;
	}
#ifdef SERIAL_DEBUG
	Serial.print(F("Solar Voltage: "));
	Serial.print(solar_voltage);
	Serial.println("V");
#endif

}

Programmierung - PC

Auf dem PC nutze ich FHEM zum empfangen und senden der Mysensors-Nachrichten. Es werde aber auch noch eine ganze Reihe an Systemen untersützt.

MySensor device

briefkasten_fhem

defmod MYSENSOR_101 MYSENSORS_DEVICE 101
attr MYSENSOR_101 DbLogInclude (humidity1|temperature2|tripped):300,batterylevel:3600
attr MYSENSOR_101 IODev MySensorGw
attr MYSENSOR_101 alias Briefkasten
attr MYSENSOR_101 devStateIcon on:message_mail_open off:message_mail
attr MYSENSOR_101 event-min-interval tripped:600
attr MYSENSOR_101 event-on-change-reading batterylevel,Taupunkt,absolute_luftfeuchtigkeit,humidity1,temperature2,tripped,voltage3,druck_pressure,druck_temperature,solar_voltage
attr MYSENSOR_101 event-on-update-reading SKETCH_.*,parentId
attr MYSENSOR_101 mapReading_druck_pressure 3 pressure
attr MYSENSOR_101 mapReading_druck_temperature 4 temperature
attr MYSENSOR_101 mapReading_humidity1 1 humidity
attr MYSENSOR_101 mapReading_solar_voltage 5 voltage
attr MYSENSOR_101 mapReading_temperature2 2 temperature
attr MYSENSOR_101 mapReading_tripped 0 tripped
attr MYSENSOR_101 mode node
attr MYSENSOR_101 room MySensors
attr MYSENSOR_101 setCommands on:tripped:on off:tripped:off
attr MYSENSOR_101 stateFormat tripped
attr MYSENSOR_101 userReadings absolute_luftfeuchtigkeit:temperature2.* {\
absLuftfeuchte(ReadingsVal("MYSENSOR_101","temperature2",20.0)\
,ReadingsVal("MYSENSOR_101","humidity1",50.0))\
},\
Taupunkt:temperature2.* {\
Taupunkt(ReadingsVal("MYSENSOR_101","temperature2",20.0)\
,ReadingsVal("MYSENSOR_101","humidity1",50.0))\
},\
\
batteryvoltage:batterylevel.* {\
int(100*(ReadingsNum("MYSENSOR_101","batterylevel",0)/100.0*(1.0)+3.2))/100.0;;\
}

Pushnachricht

Wenn nun jemand den Deckel vom Briefkasten öffnet, wird dieser über den Interrupt sofort geweckt und schickt die nachricht per mysensor an FHEM.
Hier wird am reading: tripped ein Event ausgelöst. Auf diesen Event wird mit einem Notify-Device eine Meldung abgesetzt.

Notify - Device

briefkasten_fhem_notify
Das Notify-Device lauscht auf MYSENSOR_101:tripped:.*on.*

defmod NBriefkasten notify MYSENSOR_101:tripped:.*on.* {\
fhem "set Telegram message \@Hermann_Kaiser ✉️ Post ist da!";;\
fhem "set Telegram message \@Julia_Kaiser ✉️ Post ist da!";;\
}
attr NBriefkasten disabledForIntervals 19:00-24:00 00:00-07:00

Telegram

Wie man Telegram einrichtet erkläre ich in einem anderen Post. Hier gibt es aber ein gutes Wiki von fhem.
Der Botfather wird euch schon ein Angebot machen, was ihr nicht ablehnen könnt.