Skip to content

Commit

Permalink
Merge pull request #1 from kaulketh/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
kaulketh authored Oct 1, 2021
2 parents f279bc0 + 4f427eb commit 277dbb2
Show file tree
Hide file tree
Showing 15 changed files with 333 additions and 1 deletion.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# RingPi-Bot
# RingPi-Bot

When the doorbell rings, a message is sent to a private Telegram chat group.

Based on RaspBerryPi Zero and switching a GPIO via optocoupler through 12VAC
from the doorbell, see [wiring.png](hardware/wiring.png)



10 changes: 10 additions & 0 deletions bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# __init__.py
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
from .ring_bot import *
from .singleton import *
112 changes: 112 additions & 0 deletions bot/ring_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# bot
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
import os
import signal
import time
from multiprocessing import Process

import telepot
from telepot.loop import MessageLoop

from bot import singleton
from config import RING_BOT_TOKEN, RING_BOT_NAME, RING_RING_GROUP, \
THK # no public deployment (secret.py)
from config import switch_state, DING_DONG, WELCOME, RUNNING, STOPPED, \
UNKNOWN_CMD, UNKNOWN_TYPE, CMD_START, CMD_STOP, CMD_REBOOT, REBOOT, \
START, STARTED, STOPPING
from logger import LOGGER


class RingBot(singleton.Singleton):
""" Bot class using telepot framework
(https://telepot.readthedocs.io),
Python >= 3
"""

def __init__(self, token, admin):
self.__log = LOGGER
self.__log.debug(f"Initialize instance of {self.__class__.__name__}")
self.__token = token
self.__admin = admin
self.__bot = telepot.Bot(self.__token)
self.__ding_dong = DING_DONG.format(RING_BOT_NAME)
self.__receiver = RING_RING_GROUP
self.__checker = None

def __check_bell(self, timeout=.25):
while True:
if switch_state():
self.__log.info(switch_state())
self.__send(self.__receiver, self.__ding_dong)
time.sleep(timeout)

def __send(self, chat_id, text):
self.__log.debug(
f"Message posted: "
f"{chat_id}|{text}".replace("\n", " "))
self.__bot.sendMessage(chat_id, text)

def __handle(self, msg):
content_type, chat_type, chat_id = telepot.glance(msg)
self.__log.debug(msg)
# check user
if chat_id != self.__admin:
# TODO: wrong id
pass
return
# check content
if content_type == 'text':
command = msg['text']
self.__log.info(f"Got command '{command}'")
# commands
# start
if command == CMD_START:
if self.__checker is None:
self.__checker = Process(target=self.__check_bell)
self.__checker.start()
self.__send(self.__admin, STARTED)
self.__send(self.__admin, RUNNING)
# stop
elif command == CMD_STOP:
if isinstance(self.__checker, Process):
self.__checker.terminate()
self.__checker = None
self.__send(self.__admin, STOPPING)
self.__send(self.__admin, STOPPED)
elif command == CMD_REBOOT:
self.__send(self.__admin, REBOOT.format(RING_BOT_NAME))
os.system("sudo reboot")
else:
self.__send(self.__admin, UNKNOWN_CMD)
else:
self.__send(self.__admin, UNKNOWN_TYPE)

def start(self):
try:
MessageLoop(self.__bot,
{'chat': self.__handle}).run_as_thread()
self.__log.info(START)
self.__send(self.__admin, WELCOME.format(RING_BOT_NAME))
while True:
try:
signal.pause()
except KeyboardInterrupt:
self.__log.warning('Program interrupted')
exit()
except Exception as e:
self.__log.error(f"An error occurred: {e}")
exit()


def run():
RingBot(RING_BOT_TOKEN, THK).start()


if __name__ == '__main__':
pass
21 changes: 21 additions & 0 deletions bot/singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# singleton
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
class _Singleton(type):
""" A metaclass that creates a Singleton base class when called. """
_instances = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(_Singleton, cls).__call__(*args,
**kwargs)
return cls._instances[cls]


class Singleton(_Singleton('SingletonMeta', (object,), {})):
pass
11 changes: 11 additions & 0 deletions config/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# __init__.py
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
from .constants import *
from .gpio import *
from .secret import * # no public deployment
33 changes: 33 additions & 0 deletions config/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# constants
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
CHECK_NAME = "Klingel-Überwachung"

CMD_START = "/start"
CMD_STOP = "/stop"
CMD_REBOOT = "/reboot"

UNKNOWN_CMD = "UNKNOWN COMMAND!"
UNKNOWN_TYPE = "UNKNOWN CONTENT TYPE!"

REBOOT = "{} wird neu gestarte!"
START = "Bot is running..."
WELCOME = "{} einsatzbereit!\n Starten mit '/start'!"

STARTED = f"{CHECK_NAME} gestartet."
RUNNING = f"{CHECK_NAME} läuft..."
STOPPING = f"{CHECK_NAME} wird angehalten."
STOPPED = f"{CHECK_NAME} gestoppt!"

DING_DONG = "{}\n\n\U0001F514 DING DONG \U0001F514\nEs hat an der Tür " \
"geklingelt\U00002755"

# CMD_LIST_BOT_FATHER =
# start - Start Klingel-Check
# stop - Stop Klingel-Check
# reboot - Reboot Thk1220RingBot
21 changes: 21 additions & 0 deletions config/gpio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# gpio
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
import RPi.GPIO as GPIO

PIN = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN, GPIO.IN)


def switch_state():
return False if GPIO.input(PIN) == 0 else True


if __name__ == '__main__':
pass
Binary file added hardware/rpi_zero_gpio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added hardware/wiring.fzz
Binary file not shown.
Binary file added hardware/wiring.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions logger/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# __init__.py
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
from .logger import get_logger

LOGGER = get_logger()
22 changes: 22 additions & 0 deletions logger/debug.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[loggers]
keys = root

[handlers]
keys = consoleHandler

[formatters]
keys = sampleFormatter

[logger_root]
level = DEBUG
handlers = consoleHandler

[handler_consoleHandler]
class = StreamHandler
level = DEBUG
formatter = sampleFormatter
args = (sys.stdout,)

[formatter_sampleFormatter]
format = %(asctime)s %(levelname)-7s %(module)s.%(funcName)s (linenr.%(lineno)s) %(message)s
datefmt = %Y-%m-%d %H:%M:%S
70 changes: 70 additions & 0 deletions logger/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# logger
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
import errno
import logging
import os
from logging.config import fileConfig

# runtime location
this_folder = os.path.dirname(os.path.abspath(__file__))
# define log folder related to location
log_folder = os.path.join(this_folder, '../logs')

# define ini and log files
ini_file = 'debug.ini'
info_log_file = log_folder + '/info.log'
error_log_file = log_folder + '/error.log'

# check if exists or create log folder
try:
os.makedirs(log_folder, exist_ok=True) # Python>3.2
except TypeError:
try:
os.makedirs(log_folder)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(log_folder):
pass
else:
raise

# setup configuration
config_file = os.path.join(this_folder, ini_file)
fileConfig(config_file, disable_existing_loggers=True)

# create handlers
handler_info = logging.FileHandler(os.path.join(this_folder, info_log_file))
handler_error = logging.FileHandler(os.path.join(this_folder, error_log_file))
# set levels
handler_info.setLevel(logging.INFO)
handler_error.setLevel(logging.ERROR)

# create formatters and add to handlers
format_info = \
logging.Formatter('%(asctime)s %(levelname)s '
'[ %(module)s.%(funcName)s linenr.%(lineno)s ] '
'%(message).180s', datefmt='%Y-%m-%d %H:%M:%S')
format_error = \
logging.Formatter(
'%(asctime)s %(levelname)s '
'[ %(module)s.%(funcName)s linenr.%(lineno)s ] '
'[ thread: %(threadName)s ] %(message)s')
handler_info.setFormatter(format_info)
handler_error.setFormatter(format_error)


def get_logger(name: str = __name__):
logger = logging.getLogger(name)
# add handler
logger.addHandler(handler_info)
logger.addHandler(handler_error)
return logger


if __name__ == '__main__':
pass
12 changes: 12 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------
# main
# created 01.10.2021
# Thomas Kaulke, [email protected]
# https://github.com/kaulketh
# -----------------------------------------------------------
from bot import run

if __name__ == '__main__':
run()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
telepot~=12.7

0 comments on commit 277dbb2

Please sign in to comment.