t
This commit is contained in:
parent
bf4b02902b
commit
f468332843
290
.gitignore
vendored
290
.gitignore
vendored
@ -1,145 +1,145 @@
|
|||||||
**/VE
|
**/VE
|
||||||
|
|
||||||
#VS CODE files
|
#VS CODE files
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# Persistence
|
# Persistence
|
||||||
prst.*
|
prst.*
|
||||||
|
|
||||||
log.txt
|
log.txt
|
||||||
# API-Key (keep secret at all times)
|
# API-Key (keep secret at all times)
|
||||||
keys.py
|
keys.py
|
||||||
|
|
||||||
# Cookies
|
# Cookies
|
||||||
.google-cookie
|
.google-cookie
|
||||||
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
lib/
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
wheels/
|
wheels/
|
||||||
pip-wheel-metadata/
|
pip-wheel-metadata/
|
||||||
share/python-wheels/
|
share/python-wheels/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
|
||||||
# PyInstaller
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# Usually these files are written by a python script from a template
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
*.manifest
|
*.manifest
|
||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
# Installer logs
|
# Installer logs
|
||||||
pip-log.txt
|
pip-log.txt
|
||||||
pip-delete-this-directory.txt
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
# Unit test / coverage reports
|
# Unit test / coverage reports
|
||||||
htmlcov/
|
htmlcov/
|
||||||
.tox/
|
.tox/
|
||||||
.nox/
|
.nox/
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
.cache
|
.cache
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*.cover
|
*.cover
|
||||||
*.py,cover
|
*.py,cover
|
||||||
.hypothesis/
|
.hypothesis/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
*.pot
|
*.pot
|
||||||
|
|
||||||
# Django stuff:
|
# Django stuff:
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
local_settings.py
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
db.sqlite3-journal
|
db.sqlite3-journal
|
||||||
|
|
||||||
# Flask stuff:
|
# Flask stuff:
|
||||||
instance/
|
instance/
|
||||||
.webassets-cache
|
.webassets-cache
|
||||||
|
|
||||||
# Scrapy stuff:
|
# Scrapy stuff:
|
||||||
.scrapy
|
.scrapy
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# PyBuilder
|
# PyBuilder
|
||||||
target/
|
target/
|
||||||
|
|
||||||
# Jupyter Notebook
|
# Jupyter Notebook
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
|
||||||
# IPython
|
# IPython
|
||||||
profile_default/
|
profile_default/
|
||||||
ipython_config.py
|
ipython_config.py
|
||||||
|
|
||||||
# pyenv
|
# pyenv
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
# pipenv
|
# pipenv
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
# install all needed dependencies.
|
# install all needed dependencies.
|
||||||
#Pipfile.lock
|
#Pipfile.lock
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
__pypackages__/
|
__pypackages__/
|
||||||
|
|
||||||
# Celery stuff
|
# Celery stuff
|
||||||
celerybeat-schedule
|
celerybeat-schedule
|
||||||
celerybeat.pid
|
celerybeat.pid
|
||||||
|
|
||||||
# SageMath parsed files
|
# SageMath parsed files
|
||||||
*.sage.py
|
*.sage.py
|
||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
.venv
|
.venv
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
.spyproject
|
.spyproject
|
||||||
|
|
||||||
# Rope project settings
|
# Rope project settings
|
||||||
.ropeproject
|
.ropeproject
|
||||||
|
|
||||||
# mkdocs documentation
|
# mkdocs documentation
|
||||||
/site
|
/site
|
||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
.dmypy.json
|
.dmypy.json
|
||||||
dmypy.json
|
dmypy.json
|
||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "sql_as_rest_api"]
|
|
||||||
path = sql_as_rest_api
|
|
||||||
url = https://github.com/moll-re/sql_as_rest_api
|
|
57
README.md
57
README.md
@ -1,23 +1,34 @@
|
|||||||
# AIO
|
# AIO
|
||||||
|
|
||||||
Just like AIO-coolers, this little program aims to tackle many problems at once.
|
Just like AIO-coolers, this little program aims to tackle many problems at once.
|
||||||
|
|
||||||
|
|
||||||
## What it mainly does
|
## What it mainly does
|
||||||
* chat-bot (via telegram)
|
* chat-bot (via telegram)
|
||||||
* clock and basic display (via LED-Matrix (size to taste))
|
* clock and basic display (via LED-Matrix (size to taste))
|
||||||
* dashboard (via external browser)
|
* measure ambient temperatures
|
||||||
|
* Logging of the previous actions
|
||||||
|
|
||||||
### Chatbot
|
|
||||||
Periodically calls the telegram api and reacts to sent commands. Also handles basic calls to the hardware: it allows you to control certain aspects of the clock.
|
### Chatbot
|
||||||
|
Periodically calls the telegram api and reacts to sent commands. Also handles basic calls to the hardware: it allows you to control certain aspects of the clock.
|
||||||
TODO: advanced analytics of the chat (grafana)
|
|
||||||
|
|
||||||
## Clock
|
### Clock
|
||||||
Server/Client which send/receive the output to show on the clock. Normally this is just a combination of time + weather. But special output can be triggered by the user.
|
Server/Client which send/receive the output to show on the clock. Normally this is just a combination of time + weather. But special output can be triggered by the user.
|
||||||
|
|
||||||
## Dashboard
|
### Ambient measurements
|
||||||
Shows basic info of the program and other useful things.
|
Logs temperature, luminosity, humidity to a remote database. This information is then displayed in `moll.re`.
|
||||||
|
|
||||||
TODO: show advanced host stats (cpu/mem/...)
|
TODO: Log relevant worker info such as cpu activity and network connectivity.
|
||||||
|
|
||||||
|
|
||||||
|
## Submodules
|
||||||
|
This program makes use of git submodules, namely `sql_as_rest_api`. This implies additional steps when cloning this repo:
|
||||||
|
|
||||||
|
* CLone **this** repo to your machine.
|
||||||
|
* Enter the repo
|
||||||
|
* Type `git submodule init` which creates a `.gitmodules` file
|
||||||
|
* Type `git submodule update` which fetches the newest version of these submodules
|
||||||
|
|
||||||
|
TODO Describe dev process
|
@ -1 +1 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from . import keys
|
from . import keys
|
||||||
from . import reddit
|
from . import reddit
|
||||||
from . import weather
|
from . import weather
|
||||||
from . import reddit
|
from . import reddit
|
||||||
from . import search
|
from . import search
|
||||||
from . import metmuseum
|
from . import metmuseum
|
@ -1,39 +1,39 @@
|
|||||||
import requests
|
import requests
|
||||||
import random
|
import random
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
|
|
||||||
class ArtFetch:
|
class ArtFetch:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.base_url = "https://collectionapi.metmuseum.org/"
|
self.base_url = "https://collectionapi.metmuseum.org/"
|
||||||
self.objects = self.fetch_objects() # chosen set of images to select randomly
|
self.objects = self.fetch_objects() # chosen set of images to select randomly
|
||||||
|
|
||||||
|
|
||||||
def fetch_objects(self):
|
def fetch_objects(self):
|
||||||
"""We restrict ourselves to a few domains."""
|
"""We restrict ourselves to a few domains."""
|
||||||
# fetch all departements
|
# fetch all departements
|
||||||
t = requests.get(self.base_url + "public/collection/v1/departments").json()
|
t = requests.get(self.base_url + "public/collection/v1/departments").json()
|
||||||
deps = t["departments"]
|
deps = t["departments"]
|
||||||
keep_id = []
|
keep_id = []
|
||||||
for d in deps:
|
for d in deps:
|
||||||
name = d["displayName"]
|
name = d["displayName"]
|
||||||
if name == "American Decorative Arts" or name == "Arts of Africa, Oceania, and the Americas" or name == "Asian Art" or name == "European Paintings":
|
if name == "American Decorative Arts" or name == "Arts of Africa, Oceania, and the Americas" or name == "Asian Art" or name == "European Paintings":
|
||||||
keep_id.append(str(d["departmentId"]))
|
keep_id.append(str(d["departmentId"]))
|
||||||
# fetch artworks listed under these departments
|
# fetch artworks listed under these departments
|
||||||
data = {"departmentIds" : "|".join(keep_id)}
|
data = {"departmentIds" : "|".join(keep_id)}
|
||||||
t = requests.get(self.base_url + "public/collection/v1/objects",params=data).json()
|
t = requests.get(self.base_url + "public/collection/v1/objects",params=data).json()
|
||||||
# num = t["total"]
|
# num = t["total"]
|
||||||
ids = t["objectIDs"]
|
ids = t["objectIDs"]
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def get_random_art(self):
|
def get_random_art(self):
|
||||||
"""Returns an image object of a randomly selected artwork"""
|
"""Returns an image object of a randomly selected artwork"""
|
||||||
# fetch the artwork's url
|
# fetch the artwork's url
|
||||||
r_id = self.objects[random.randint(0,len(self.objects))]
|
r_id = self.objects[random.randint(0,len(self.objects))]
|
||||||
t = requests.get(self.base_url + "public/collection/v1/objects/" + str(r_id)).json()
|
t = requests.get(self.base_url + "public/collection/v1/objects/" + str(r_id)).json()
|
||||||
im_url = t["primaryImageSmall"]
|
im_url = t["primaryImageSmall"]
|
||||||
# download the image
|
# download the image
|
||||||
resp = requests.get(im_url)
|
resp = requests.get(im_url)
|
||||||
img = Image.open(io.BytesIO(resp.content))
|
img = Image.open(io.BytesIO(resp.content))
|
||||||
|
|
||||||
return img
|
return img
|
||||||
|
@ -1,56 +1,56 @@
|
|||||||
import praw
|
import praw
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RedditFetch():
|
class RedditFetch():
|
||||||
def __init__(self, key):
|
def __init__(self, key):
|
||||||
self.stream = praw.Reddit(client_id = key["id"], client_secret = key["secret"], user_agent=key["user_agent"])
|
self.stream = praw.Reddit(client_id = key["id"], client_secret = key["secret"], user_agent=key["user_agent"])
|
||||||
|
|
||||||
def get_top(self, subreddit, number, return_type="text"):
|
def get_top(self, subreddit, number, return_type="text"):
|
||||||
if return_type == "text":
|
if return_type == "text":
|
||||||
posts = []
|
posts = []
|
||||||
try:
|
try:
|
||||||
for submission in self.stream.subreddit(subreddit).top(limit=number):
|
for submission in self.stream.subreddit(subreddit).top(limit=number):
|
||||||
p = {}
|
p = {}
|
||||||
if not submission.stickied:
|
if not submission.stickied:
|
||||||
p["title"] = submission.title
|
p["title"] = submission.title
|
||||||
p["content"] = submission.selftext
|
p["content"] = submission.selftext
|
||||||
posts.append(p)
|
posts.append(p)
|
||||||
return posts
|
return posts
|
||||||
except:
|
except:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
images = []
|
images = []
|
||||||
try:
|
try:
|
||||||
for submission in self.stream.subreddit(subreddit).top(limit=number):
|
for submission in self.stream.subreddit(subreddit).top(limit=number):
|
||||||
if not submission.stickied:
|
if not submission.stickied:
|
||||||
t = {"image": submission.url, "caption": submission.title}
|
t = {"image": submission.url, "caption": submission.title}
|
||||||
images.append(t)
|
images.append(t)
|
||||||
return images
|
return images
|
||||||
except:
|
except:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def get_random_rising(self, subreddit, number, return_type="text"):
|
def get_random_rising(self, subreddit, number, return_type="text"):
|
||||||
if return_type == "text":
|
if return_type == "text":
|
||||||
posts = []
|
posts = []
|
||||||
try:
|
try:
|
||||||
for submission in self.stream.subreddit(subreddit).random_rising(limit=number):
|
for submission in self.stream.subreddit(subreddit).random_rising(limit=number):
|
||||||
p = {}
|
p = {}
|
||||||
if not submission.stickied:
|
if not submission.stickied:
|
||||||
p["title"] = submission.title
|
p["title"] = submission.title
|
||||||
p["content"] = submission.selftext
|
p["content"] = submission.selftext
|
||||||
posts.append(p)
|
posts.append(p)
|
||||||
return posts
|
return posts
|
||||||
except:
|
except:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
images = []
|
images = []
|
||||||
try:
|
try:
|
||||||
for submission in self.stream.subreddit(subreddit).random_rising(limit=number):
|
for submission in self.stream.subreddit(subreddit).random_rising(limit=number):
|
||||||
if not submission.stickied:
|
if not submission.stickied:
|
||||||
t = {"image": submission.url, "caption": submission.title}
|
t = {"image": submission.url, "caption": submission.title}
|
||||||
images.append(t)
|
images.append(t)
|
||||||
return images
|
return images
|
||||||
except:
|
except:
|
||||||
return []
|
return []
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import duckduckpy
|
import duckduckpy
|
||||||
|
|
||||||
class WebSearch():
|
class WebSearch():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.search = duckduckpy.query
|
self.search = duckduckpy.query
|
||||||
|
|
||||||
def get_result(self, query):
|
def get_result(self, query):
|
||||||
try:
|
try:
|
||||||
res = []
|
res = []
|
||||||
response = self.search(query, container = "dict")["related_topics"]
|
response = self.search(query, container = "dict")["related_topics"]
|
||||||
for r in response:
|
for r in response:
|
||||||
if "text" in r:
|
if "text" in r:
|
||||||
res.append({
|
res.append({
|
||||||
"text" : r["text"],
|
"text" : r["text"],
|
||||||
"url": r["first_url"]
|
"url": r["first_url"]
|
||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
res = ["Connection error"]
|
res = ["Connection error"]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# TODO: this api has more potential. Extract images or quick facts!
|
# TODO: this api has more potential. Extract images or quick facts!
|
@ -1,87 +1,87 @@
|
|||||||
import requests
|
import requests
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
class WeatherFetch():
|
class WeatherFetch():
|
||||||
def __init__(self, key):
|
def __init__(self, key):
|
||||||
self.last_fetch = datetime.datetime.fromtimestamp(0)
|
self.last_fetch = datetime.datetime.fromtimestamp(0)
|
||||||
self.last_weather = ""
|
self.last_weather = ""
|
||||||
self.calls = 0
|
self.calls = 0
|
||||||
|
|
||||||
self.url = "https://api.openweathermap.org/data/2.5/onecall?"
|
self.url = "https://api.openweathermap.org/data/2.5/onecall?"
|
||||||
self.key = key
|
self.key = key
|
||||||
|
|
||||||
def show_weather(self, location):
|
def show_weather(self, location):
|
||||||
delta = datetime.datetime.now() - self.last_fetch
|
delta = datetime.datetime.now() - self.last_fetch
|
||||||
if delta.total_seconds()/60 > 60 or "\n" not in self.last_weather: # 1 hour passed:
|
if delta.total_seconds()/60 > 60 or "\n" not in self.last_weather: # 1 hour passed:
|
||||||
|
|
||||||
|
|
||||||
data = {"lat" : location[0], "lon" : location[1], "exclude" : "minutely,hourly", "appid" : self.key, "units" : "metric"}
|
data = {"lat" : location[0], "lon" : location[1], "exclude" : "minutely,hourly", "appid" : self.key, "units" : "metric"}
|
||||||
self.calls += 1
|
self.calls += 1
|
||||||
logger.info("Just fetched weather. ({}th time)".format(self.calls))
|
logger.info("Just fetched weather. ({}th time)".format(self.calls))
|
||||||
# today = datetime.datetime.today().weekday()
|
# today = datetime.datetime.today().weekday()
|
||||||
# days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
# days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
weather = requests.get(self.url,params=data).json()
|
weather = requests.get(self.url,params=data).json()
|
||||||
# categories = {"Clouds": ":cloud:", "Rain": ":cloud_with_rain:", "Thunderstorm": "thunder_cloud_rain", "Drizzle": ":droplet:", "Snow": ":cloud_snow:", "Clear": ":sun:", "Mist": "Mist", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
# categories = {"Clouds": ":cloud:", "Rain": ":cloud_with_rain:", "Thunderstorm": "thunder_cloud_rain", "Drizzle": ":droplet:", "Snow": ":cloud_snow:", "Clear": ":sun:", "Mist": "Mist", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
||||||
now = weather["current"]
|
now = weather["current"]
|
||||||
ret_weather = []
|
ret_weather = []
|
||||||
ret_weather.append({
|
ret_weather.append({
|
||||||
"short" : now["weather"][0]["main"],
|
"short" : now["weather"][0]["main"],
|
||||||
"temps" : [int(now["temp"])]
|
"temps" : [int(now["temp"])]
|
||||||
})
|
})
|
||||||
weather_days = weather["daily"]
|
weather_days = weather["daily"]
|
||||||
for i, day in enumerate(weather_days):
|
for i, day in enumerate(weather_days):
|
||||||
ret_weather.append({
|
ret_weather.append({
|
||||||
"short" : day["weather"][0]["main"],
|
"short" : day["weather"][0]["main"],
|
||||||
"temps" : [int(day["temp"]["min"]),int(day["temp"]["max"])]
|
"temps" : [int(day["temp"]["min"]),int(day["temp"]["max"])]
|
||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
ret_weather = []
|
ret_weather = []
|
||||||
|
|
||||||
|
|
||||||
# now = weather["current"]
|
# now = weather["current"]
|
||||||
# message = "<b>Now:</b> " + categories[now["weather"][0]["main"]] + "\n"
|
# message = "<b>Now:</b> " + categories[now["weather"][0]["main"]] + "\n"
|
||||||
# message += ":thermometer: " + str(int(now["temp"])) + "°\n\n"
|
# message += ":thermometer: " + str(int(now["temp"])) + "°\n\n"
|
||||||
|
|
||||||
# weather_days = weather["daily"]
|
# weather_days = weather["daily"]
|
||||||
|
|
||||||
# for i, day in enumerate(weather_days):
|
# for i, day in enumerate(weather_days):
|
||||||
# if i == 0:
|
# if i == 0:
|
||||||
# message += "<b>" + "Today" + ":</b> " + categories[day["weather"][0]["main"]] + "\n"
|
# message += "<b>" + "Today" + ":</b> " + categories[day["weather"][0]["main"]] + "\n"
|
||||||
# else:
|
# else:
|
||||||
# message += "<b>" + days[(today + i + 1) % 7] + ":</b> " + categories[day["weather"][0]["main"]] + "\n"
|
# message += "<b>" + days[(today + i + 1) % 7] + ":</b> " + categories[day["weather"][0]["main"]] + "\n"
|
||||||
# message += ":thermometer: :fast_down_button: " + str(int(day["temp"]["min"])) + "° , :thermometer: :fast_up_button: " + str(int(day["temp"]["max"])) + "°\n\n"
|
# message += ":thermometer: :fast_down_button: " + str(int(day["temp"]["min"])) + "° , :thermometer: :fast_up_button: " + str(int(day["temp"]["max"])) + "°\n\n"
|
||||||
# except:
|
# except:
|
||||||
# message = "Query failed, it's my fault, I'm sorry :sad:"
|
# message = "Query failed, it's my fault, I'm sorry :sad:"
|
||||||
|
|
||||||
self.last_weather = ret_weather
|
self.last_weather = ret_weather
|
||||||
self.last_fetch = datetime.datetime.now()
|
self.last_fetch = datetime.datetime.now()
|
||||||
else:
|
else:
|
||||||
ret_weather = self.last_weather
|
ret_weather = self.last_weather
|
||||||
|
|
||||||
return ret_weather
|
return ret_weather
|
||||||
|
|
||||||
# def get_weather_by_city(self, city):
|
# def get_weather_by_city(self, city):
|
||||||
# loc = get_coords_from_city(self, city)
|
# loc = get_coords_from_city(self, city)
|
||||||
# weather = self.show_weather(loc)
|
# weather = self.show_weather(loc)
|
||||||
# return weather
|
# return weather
|
||||||
|
|
||||||
|
|
||||||
# def get_coords_from_city(self, city):
|
# def get_coords_from_city(self, city):
|
||||||
# url = "https://devru-latitude-longitude-find-v1.p.rapidapi.com/latlon.php"
|
# url = "https://devru-latitude-longitude-find-v1.p.rapidapi.com/latlon.php"
|
||||||
# data = {"location": city}
|
# data = {"location": city}
|
||||||
# headers = {
|
# headers = {
|
||||||
# "x-rapidapi-key" : "d4e0ab7ab3mshd5dde5a282649e0p11fd98jsnc93afd98e3aa",
|
# "x-rapidapi-key" : "d4e0ab7ab3mshd5dde5a282649e0p11fd98jsnc93afd98e3aa",
|
||||||
# "x-rapidapi-host" : "devru-latitude-longitude-find-v1.p.rapidapi.com",
|
# "x-rapidapi-host" : "devru-latitude-longitude-find-v1.p.rapidapi.com",
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# #try:
|
# #try:
|
||||||
# resp = requests.request("GET", url, headers=headers, params=data)
|
# resp = requests.request("GET", url, headers=headers, params=data)
|
||||||
# result = resp.text
|
# result = resp.text
|
||||||
# #except:
|
# #except:
|
||||||
# # result = "???"
|
# # result = "???"
|
||||||
# return result
|
# return result
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
from . import clock, help, weather, status, zvv, lists, alias, plaintext, reddit, search
|
from . import clock, help, weather, status, zvv, lists, alias, plaintext, reddit, search
|
||||||
|
@ -1,64 +1,64 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
FIRST = range(1)
|
FIRST = range(1)
|
||||||
class Alias(BotFunc):
|
class Alias(BotFunc):
|
||||||
"""create a new command for command-paths you often use"""
|
"""create a new command for command-paths you often use"""
|
||||||
|
|
||||||
def __init__(self, dispatcher, db):
|
def __init__(self, dispatcher, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.dispatcher = dispatcher
|
self.dispatcher = dispatcher
|
||||||
# do not interact with him yet!
|
# do not interact with him yet!
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('alias', self.entry_point)],
|
entry_points=[CommandHandler('alias', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
FIRST: [
|
FIRST: [
|
||||||
CallbackQueryHandler(self.print_all, pattern="^all$"),
|
CallbackQueryHandler(self.print_all, pattern="^all$"),
|
||||||
CallbackQueryHandler(self.create_alias, pattern="^new$"),
|
CallbackQueryHandler(self.create_alias, pattern="^new$"),
|
||||||
CallbackQueryHandler(self.delete_alias, pattern='^delete$'),
|
CallbackQueryHandler(self.delete_alias, pattern='^delete$'),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('alias', self.entry_point)],
|
fallbacks=[CommandHandler('alias', self.entry_point)],
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
test = self.dispatcher
|
test = self.dispatcher
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[InlineKeyboardButton("All aliases", callback_data="all")],
|
[InlineKeyboardButton("All aliases", callback_data="all")],
|
||||||
[InlineKeyboardButton("Create new alias", callback_data="new")],
|
[InlineKeyboardButton("Create new alias", callback_data="new")],
|
||||||
[InlineKeyboardButton("Delete alias", callback_data="delete")],
|
[InlineKeyboardButton("Delete alias", callback_data="delete")],
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
super().log_activity(receive=True, execute=False, send=True)
|
super().log_activity(receive=True, execute=False, send=True)
|
||||||
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
||||||
return FIRST
|
return FIRST
|
||||||
|
|
||||||
|
|
||||||
def print_all(self, update: Update, context: CallbackContext) -> None:
|
def print_all(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
all_alias = ""
|
all_alias = ""
|
||||||
for k in self.persistence["bot"]["aliases"]:
|
for k in self.persistence["bot"]["aliases"]:
|
||||||
all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n"
|
all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n"
|
||||||
|
|
||||||
query.edit_message_text(text="List of all commands:\n" + all_alias)
|
query.edit_message_text(text="List of all commands:\n" + all_alias)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def create_alias(self, update: Update, context: CallbackContext) -> None:
|
def create_alias(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
all_alias = ""
|
all_alias = ""
|
||||||
for k in self.persistence["bot"]["aliases"]:
|
for k in self.persistence["bot"]["aliases"]:
|
||||||
all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n"
|
all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n"
|
||||||
|
|
||||||
query.edit_message_text(text="List of all commands:\n" + all_alias)
|
query.edit_message_text(text="List of all commands:\n" + all_alias)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
def delete_alias(self, update: Update, context: CallbackContext) -> None:
|
def delete_alias(self, update: Update, context: CallbackContext) -> None:
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
@ -1,218 +1,218 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
import time
|
import time
|
||||||
import numpy
|
import numpy
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
|
|
||||||
CHOOSE, ADDARG = range(2)
|
CHOOSE, ADDARG = range(2)
|
||||||
MESSAGE, WAKE, ALARM, IMAGE, ART = range(3,8)
|
MESSAGE, WAKE, ALARM, IMAGE, ART = range(3,8)
|
||||||
|
|
||||||
class Clock(BotFunc):
|
class Clock(BotFunc):
|
||||||
"""pass on commands to clock-module"""
|
"""pass on commands to clock-module"""
|
||||||
def __init__(self, prst, clock_module, art_api):
|
def __init__(self, prst, clock_module, art_api):
|
||||||
super().__init__(prst)
|
super().__init__(prst)
|
||||||
self.clock = clock_module
|
self.clock = clock_module
|
||||||
self.api_art = art_api
|
self.api_art = art_api
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
handler = ConversationHandler(
|
handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler("clock", self.entry_point)],
|
entry_points=[CommandHandler("clock", self.entry_point)],
|
||||||
states={
|
states={
|
||||||
CHOOSE : [
|
CHOOSE : [
|
||||||
CallbackQueryHandler(self.wake_light, pattern="^wake$"),
|
CallbackQueryHandler(self.wake_light, pattern="^wake$"),
|
||||||
CallbackQueryHandler(self.alarm_blink, pattern="^alarm$"),
|
CallbackQueryHandler(self.alarm_blink, pattern="^alarm$"),
|
||||||
CallbackQueryHandler(self.show_message, pattern="^message$"),
|
CallbackQueryHandler(self.show_message, pattern="^message$"),
|
||||||
CallbackQueryHandler(self.show_image, pattern="^image$"),
|
CallbackQueryHandler(self.show_image, pattern="^image$"),
|
||||||
CallbackQueryHandler(self.art_gallery, pattern="^gallery$"),
|
CallbackQueryHandler(self.art_gallery, pattern="^gallery$"),
|
||||||
],
|
],
|
||||||
ADDARG : [MessageHandler(Filters.text, callback=self.get_arg1)],
|
ADDARG : [MessageHandler(Filters.text, callback=self.get_arg1)],
|
||||||
MESSAGE: [MessageHandler(Filters.text, callback=self.exec_show_message)],
|
MESSAGE: [MessageHandler(Filters.text, callback=self.exec_show_message)],
|
||||||
WAKE : [MessageHandler(Filters.text, callback=self.exec_wake_light)],
|
WAKE : [MessageHandler(Filters.text, callback=self.exec_wake_light)],
|
||||||
ALARM : [MessageHandler(Filters.text, callback=self.exec_alarm_blink)],
|
ALARM : [MessageHandler(Filters.text, callback=self.exec_alarm_blink)],
|
||||||
IMAGE : [MessageHandler(Filters.photo, callback=self.exec_show_image)],
|
IMAGE : [MessageHandler(Filters.photo, callback=self.exec_show_image)],
|
||||||
ART : [MessageHandler(Filters.text, callback=self.exec_art_gallery)],
|
ART : [MessageHandler(Filters.text, callback=self.exec_art_gallery)],
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('clock', self.entry_point)],
|
fallbacks=[CommandHandler('clock', self.entry_point)],
|
||||||
)
|
)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[InlineKeyboardButton("Make a wake-light", callback_data="wake")],
|
[InlineKeyboardButton("Make a wake-light", callback_data="wake")],
|
||||||
[InlineKeyboardButton("Blink as alarm", callback_data="alarm")],
|
[InlineKeyboardButton("Blink as alarm", callback_data="alarm")],
|
||||||
[InlineKeyboardButton("Show a message", callback_data="message")],
|
[InlineKeyboardButton("Show a message", callback_data="message")],
|
||||||
[InlineKeyboardButton("Show an image", callback_data="image")],
|
[InlineKeyboardButton("Show an image", callback_data="image")],
|
||||||
[InlineKeyboardButton("Art gallery!", callback_data="gallery")],
|
[InlineKeyboardButton("Art gallery!", callback_data="gallery")],
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
||||||
return CHOOSE
|
return CHOOSE
|
||||||
|
|
||||||
|
|
||||||
def wake_light(self, update: Update, context: CallbackContext) -> None:
|
def wake_light(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
query.edit_message_text("Ok. How long should the color cycle last? (In seconds)")
|
query.edit_message_text("Ok. How long should the color cycle last? (In seconds)")
|
||||||
return WAKE
|
return WAKE
|
||||||
|
|
||||||
def alarm_blink(self, update: Update, context: CallbackContext) -> None:
|
def alarm_blink(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
query.edit_message_text("Ok. How long should it blink? (In seconds)")
|
query.edit_message_text("Ok. How long should it blink? (In seconds)")
|
||||||
self.next_state = {ALARM : "What frequency (Hertz)"}
|
self.next_state = {ALARM : "What frequency (Hertz)"}
|
||||||
return ADDARG
|
return ADDARG
|
||||||
|
|
||||||
def show_message(self, update: Update, context: CallbackContext) -> None:
|
def show_message(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
query.edit_message_text("Ok. What message will I show?")
|
query.edit_message_text("Ok. What message will I show?")
|
||||||
return MESSAGE
|
return MESSAGE
|
||||||
|
|
||||||
def show_image(self, update: Update, context: CallbackContext) -> None:
|
def show_image(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
query.edit_message_text("How long (in minutes) should the image be displayed?")
|
query.edit_message_text("How long (in minutes) should the image be displayed?")
|
||||||
self.next_state = {IMAGE : "Please send me the photo to display."}
|
self.next_state = {IMAGE : "Please send me the photo to display."}
|
||||||
return ADDARG
|
return ADDARG
|
||||||
|
|
||||||
def art_gallery(self, update: Update, context: CallbackContext) -> None:
|
def art_gallery(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
query.edit_message_text("Ok. How long should we display art? (in hours")
|
query.edit_message_text("Ok. How long should we display art? (in hours")
|
||||||
self.next_state = {ART : "And how many artworks would you like to see during that time?"}
|
self.next_state = {ART : "And how many artworks would you like to see during that time?"}
|
||||||
return ADDARG
|
return ADDARG
|
||||||
|
|
||||||
def get_arg1(self, update: Update, context: CallbackContext) -> None:
|
def get_arg1(self, update: Update, context: CallbackContext) -> None:
|
||||||
a = update.message.text
|
a = update.message.text
|
||||||
self.additional_argument = a
|
self.additional_argument = a
|
||||||
update.message.reply_text("Furthermore: "+ list(self.next_state.values())[0])
|
update.message.reply_text("Furthermore: "+ list(self.next_state.values())[0])
|
||||||
return list(self.next_state.keys())[0]
|
return list(self.next_state.keys())[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###### actually running clock actions
|
###### actually running clock actions
|
||||||
def exec_wake_light(self, update: Update, context: CallbackContext) -> None:
|
def exec_wake_light(self, update: Update, context: CallbackContext) -> None:
|
||||||
duration = update.message.text
|
duration = update.message.text
|
||||||
|
|
||||||
def output(duration):
|
def output(duration):
|
||||||
self.clock.set_brightness(value=1)
|
self.clock.set_brightness(value=1)
|
||||||
start_color = numpy.array([153, 0, 51])
|
start_color = numpy.array([153, 0, 51])
|
||||||
end_color = numpy.array([255, 255, 0])
|
end_color = numpy.array([255, 255, 0])
|
||||||
col_show = numpy.zeros((*self.clock.shape, 3))
|
col_show = numpy.zeros((*self.clock.shape, 3))
|
||||||
col_show[:,:,...] = start_color
|
col_show[:,:,...] = start_color
|
||||||
|
|
||||||
gradient = end_color - start_color
|
gradient = end_color - start_color
|
||||||
# 20 steps should be fine => sleep_time = duration / 20
|
# 20 steps should be fine => sleep_time = duration / 20
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
ct = i/20 * gradient
|
ct = i/20 * gradient
|
||||||
col_show[:,:,...] = [int(x) for x in ct+start_color]
|
col_show[:,:,...] = [int(x) for x in ct+start_color]
|
||||||
self.clock.IO.put(col_show)
|
self.clock.IO.put(col_show)
|
||||||
time.sleep(int(duration) / 20)
|
time.sleep(int(duration) / 20)
|
||||||
|
|
||||||
self.clock.run(output,(duration,))
|
self.clock.run(output,(duration,))
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def exec_alarm_blink(self, update: Update, context: CallbackContext) -> None:
|
def exec_alarm_blink(self, update: Update, context: CallbackContext) -> None:
|
||||||
duration = self.additional_argument
|
duration = self.additional_argument
|
||||||
frequency = update.message.text
|
frequency = update.message.text
|
||||||
|
|
||||||
def output(duration, frequency):
|
def output(duration, frequency):
|
||||||
self.clock.set_brightness(value=1)
|
self.clock.set_brightness(value=1)
|
||||||
duration = int(duration)
|
duration = int(duration)
|
||||||
frequency = int(frequency)
|
frequency = int(frequency)
|
||||||
n = duration * frequency / 2
|
n = duration * frequency / 2
|
||||||
empty = numpy.zeros((*self.clock.shape,3))
|
empty = numpy.zeros((*self.clock.shape,3))
|
||||||
red = empty.copy()
|
red = empty.copy()
|
||||||
red[...,0] = 255
|
red[...,0] = 255
|
||||||
for i in range(int(n)):
|
for i in range(int(n)):
|
||||||
self.clock.IO.put(red)
|
self.clock.IO.put(red)
|
||||||
time.sleep(1/frequency)
|
time.sleep(1/frequency)
|
||||||
self.clock.IO.put(empty)
|
self.clock.IO.put(empty)
|
||||||
time.sleep(1/frequency)
|
time.sleep(1/frequency)
|
||||||
|
|
||||||
if not(duration == 0 or frequency == 0):
|
if not(duration == 0 or frequency == 0):
|
||||||
update.message.reply_text("Now blinking")
|
update.message.reply_text("Now blinking")
|
||||||
self.clock.run(output,(duration, frequency))
|
self.clock.run(output,(duration, frequency))
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def exec_show_image(self, update: Update, context: CallbackContext) -> None:
|
def exec_show_image(self, update: Update, context: CallbackContext) -> None:
|
||||||
duration = self.additional_argument
|
duration = self.additional_argument
|
||||||
i = update.message.photo
|
i = update.message.photo
|
||||||
img = update.message.photo[0]
|
img = update.message.photo[0]
|
||||||
bot = img.bot
|
bot = img.bot
|
||||||
id = img.file_id
|
id = img.file_id
|
||||||
|
|
||||||
file = bot.getFile(id).download_as_bytearray()
|
file = bot.getFile(id).download_as_bytearray()
|
||||||
width = self.clock.shape[1]
|
width = self.clock.shape[1]
|
||||||
height = self.clock.shape[0]
|
height = self.clock.shape[0]
|
||||||
|
|
||||||
img = Image.open(io.BytesIO(file))
|
img = Image.open(io.BytesIO(file))
|
||||||
im_height = img.height
|
im_height = img.height
|
||||||
im_width = img.width
|
im_width = img.width
|
||||||
|
|
||||||
scalex = im_width // width
|
scalex = im_width // width
|
||||||
scaley = im_height // height
|
scaley = im_height // height
|
||||||
scale = min(scalex, scaley)
|
scale = min(scalex, scaley)
|
||||||
|
|
||||||
t = img.resize((width, height),box=(0,0,width*scale,height*scale))
|
t = img.resize((width, height),box=(0,0,width*scale,height*scale))
|
||||||
a = numpy.asarray(t)
|
a = numpy.asarray(t)
|
||||||
|
|
||||||
def output(image, duration):
|
def output(image, duration):
|
||||||
self.clock.IO.put(image)
|
self.clock.IO.put(image)
|
||||||
time.sleep(int(duration) * 60)
|
time.sleep(int(duration) * 60)
|
||||||
|
|
||||||
self.clock.run(output,(a, duration))
|
self.clock.run(output,(a, duration))
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def exec_show_message(self, update: Update, context: CallbackContext) -> None:
|
def exec_show_message(self, update: Update, context: CallbackContext) -> None:
|
||||||
message_str = update.message.text
|
message_str = update.message.text
|
||||||
update.message.reply_text("Now showing: " + message_str)
|
update.message.reply_text("Now showing: " + message_str)
|
||||||
self.clock.run(self.clock.text_scroll,(message_str,))
|
self.clock.run(self.clock.text_scroll,(message_str,))
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def exec_art_gallery(self, update: Update, context: CallbackContext) -> None:
|
def exec_art_gallery(self, update: Update, context: CallbackContext) -> None:
|
||||||
duration = float(self.additional_argument)
|
duration = float(self.additional_argument)
|
||||||
number = int(update.message.text)
|
number = int(update.message.text)
|
||||||
|
|
||||||
def output(number, duration):
|
def output(number, duration):
|
||||||
for i in range(number):
|
for i in range(number):
|
||||||
img = self.api_art.get_random_art() # returns an PIL.Image object
|
img = self.api_art.get_random_art() # returns an PIL.Image object
|
||||||
im_height = img.height
|
im_height = img.height
|
||||||
im_width = img.width
|
im_width = img.width
|
||||||
|
|
||||||
width = self.clock.shape[1]
|
width = self.clock.shape[1]
|
||||||
height = self.clock.shape[0]
|
height = self.clock.shape[0]
|
||||||
|
|
||||||
scalex = im_width // width
|
scalex = im_width // width
|
||||||
scaley = im_height // height
|
scaley = im_height // height
|
||||||
scale = min(scalex, scaley)
|
scale = min(scalex, scaley)
|
||||||
|
|
||||||
t = img.resize((width, height),box=(0,0,width*scale,height*scale))
|
t = img.resize((width, height),box=(0,0,width*scale,height*scale))
|
||||||
a = numpy.asarray(t)
|
a = numpy.asarray(t)
|
||||||
self.clock.IO.put(a)
|
self.clock.IO.put(a)
|
||||||
|
|
||||||
time.sleep(duration*3600 / number)
|
time.sleep(duration*3600 / number)
|
||||||
|
|
||||||
|
|
||||||
update.message.reply_text("Ok. Showing art for the next "+ str(duration) + " hours.")
|
update.message.reply_text("Ok. Showing art for the next "+ str(duration) + " hours.")
|
||||||
self.clock.run(output,(number, duration))
|
self.clock.run(output,(number, duration))
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO FIx this to work with the new backend
|
# TODO FIx this to work with the new backend
|
@ -1,128 +1,128 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
FIRST, EXECUTE = range(2)
|
FIRST, EXECUTE = range(2)
|
||||||
|
|
||||||
|
|
||||||
class Help(BotFunc):
|
class Help(BotFunc):
|
||||||
"""Shows the functions and their usage"""
|
"""Shows the functions and their usage"""
|
||||||
|
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.available_commands = {}
|
self.available_commands = {}
|
||||||
|
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('help', self.entry_point)],
|
entry_points=[CommandHandler('help', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
FIRST: [
|
FIRST: [
|
||||||
CallbackQueryHandler(self.print_all, pattern="^all$"),
|
CallbackQueryHandler(self.print_all, pattern="^all$"),
|
||||||
CallbackQueryHandler(self.choose_specific, pattern="^specific$"),
|
CallbackQueryHandler(self.choose_specific, pattern="^specific$"),
|
||||||
CallbackQueryHandler(self.print_one, pattern='func-'),
|
CallbackQueryHandler(self.print_one, pattern='func-'),
|
||||||
],
|
],
|
||||||
EXECUTE :[CallbackQueryHandler(self.execute_now)],
|
EXECUTE :[CallbackQueryHandler(self.execute_now)],
|
||||||
# ConversationHandler.TIMEOUT : [
|
# ConversationHandler.TIMEOUT : [
|
||||||
# CallbackQueryHandler(self.timeout)
|
# CallbackQueryHandler(self.timeout)
|
||||||
# ]
|
# ]
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('help', self.entry_point)],
|
fallbacks=[CommandHandler('help', self.entry_point)],
|
||||||
conversation_timeout=15,
|
conversation_timeout=15,
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
def add_commands(self, commands):
|
def add_commands(self, commands):
|
||||||
# commands is a dict {"name": class}
|
# commands is a dict {"name": class}
|
||||||
for k in commands:
|
for k in commands:
|
||||||
if k != "plaintext":
|
if k != "plaintext":
|
||||||
self.available_commands[k] = commands[k].__doc__
|
self.available_commands[k] = commands[k].__doc__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
|
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("All commands", callback_data="all"),
|
InlineKeyboardButton("All commands", callback_data="all"),
|
||||||
InlineKeyboardButton("Just one", callback_data="specific"),
|
InlineKeyboardButton("Just one", callback_data="specific"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
||||||
if update.message:
|
if update.message:
|
||||||
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
|
||||||
else:
|
else:
|
||||||
update._effective_chat.send_message("What exactly do you want?", reply_markup=reply_markup)
|
update._effective_chat.send_message("What exactly do you want?", reply_markup=reply_markup)
|
||||||
return FIRST
|
return FIRST
|
||||||
|
|
||||||
|
|
||||||
def print_all(self, update: Update, context: CallbackContext) -> None:
|
def print_all(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
all_cmd = ""
|
all_cmd = ""
|
||||||
for h in self.available_commands:
|
for h in self.available_commands:
|
||||||
all_cmd += "{} - `{}`\n".format(h, self.available_commands[h])
|
all_cmd += "{} - `{}`\n".format(h, self.available_commands[h])
|
||||||
|
|
||||||
query.edit_message_text(text="List of all commands:\n" + all_cmd, parse_mode = ParseMode.MARKDOWN)
|
query.edit_message_text(text="List of all commands:\n" + all_cmd, parse_mode = ParseMode.MARKDOWN)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def choose_specific(self, update: Update, context: CallbackContext) -> None:
|
def choose_specific(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
|
|
||||||
keyboard = [[InlineKeyboardButton(k, callback_data="func-" + k)] for k in self.available_commands]
|
keyboard = [[InlineKeyboardButton(k, callback_data="func-" + k)] for k in self.available_commands]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
query.edit_message_text(
|
query.edit_message_text(
|
||||||
text="What command should be printed?", reply_markup=reply_markup
|
text="What command should be printed?", reply_markup=reply_markup
|
||||||
)
|
)
|
||||||
return FIRST
|
return FIRST
|
||||||
|
|
||||||
|
|
||||||
def print_one(self, update: Update, context: CallbackContext) -> None:
|
def print_one(self, update: Update, context: CallbackContext) -> None:
|
||||||
"""Show new choice of buttons"""
|
"""Show new choice of buttons"""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
name = query.data.replace("func-", "")
|
name = query.data.replace("func-", "")
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
message = name + ": `" + self.available_commands[name] + "`"
|
message = name + ": `" + self.available_commands[name] + "`"
|
||||||
|
|
||||||
keyboard = [[InlineKeyboardButton("Call " + name + " now", callback_data=name),]]
|
keyboard = [[InlineKeyboardButton("Call " + name + " now", callback_data=name),]]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
query.edit_message_text(
|
query.edit_message_text(
|
||||||
text= message,
|
text= message,
|
||||||
reply_markup = reply_markup,
|
reply_markup = reply_markup,
|
||||||
parse_mode = ParseMode.MARKDOWN_V2
|
parse_mode = ParseMode.MARKDOWN_V2
|
||||||
)
|
)
|
||||||
return EXECUTE
|
return EXECUTE
|
||||||
|
|
||||||
|
|
||||||
def execute_now(self, update: Update, context: CallbackContext) -> None:
|
def execute_now(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
name = query.data
|
name = query.data
|
||||||
query.answer()
|
query.answer()
|
||||||
funcs = context.dispatcher.handlers[0]
|
funcs = context.dispatcher.handlers[0]
|
||||||
for func in funcs:
|
for func in funcs:
|
||||||
if name == func.entry_points[0].command[0]:
|
if name == func.entry_points[0].command[0]:
|
||||||
break
|
break
|
||||||
callback = func.entry_points[0].handle_update
|
callback = func.entry_points[0].handle_update
|
||||||
callback(update, context.dispatcher, check_result=True, context=context)
|
callback(update, context.dispatcher, check_result=True, context=context)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def timeout(self, update: Update, context: CallbackContext) -> None:
|
def timeout(self, update: Update, context: CallbackContext) -> None:
|
||||||
"""For dying conversation. Currently unused."""
|
"""For dying conversation. Currently unused."""
|
||||||
|
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
name = query.data.replace("func-", "")
|
name = query.data.replace("func-", "")
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
message = name + ": `" + self.available_commands[name] + "`"
|
message = name + ": `" + self.available_commands[name] + "`"
|
||||||
query.edit_message_text(
|
query.edit_message_text(
|
||||||
text= "Timed out...",
|
text= "Timed out...",
|
||||||
parse_mode = ParseMode.MARKDOWN_V2
|
parse_mode = ParseMode.MARKDOWN_V2
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
@ -1,200 +1,198 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
import datetime
|
NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5)
|
||||||
import requests
|
|
||||||
|
|
||||||
NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5)
|
class Lists(BotFunc):
|
||||||
|
"""Create and edit lists"""
|
||||||
|
|
||||||
class Lists(BotFunc):
|
def __init__(self, db):
|
||||||
"""Create and edit lists"""
|
super().__init__(db)
|
||||||
|
self.current_name = ""
|
||||||
def __init__(self, db):
|
|
||||||
super().__init__(db)
|
|
||||||
self.current_name = ""
|
def create_handler(self):
|
||||||
|
conv_handler = ConversationHandler(
|
||||||
|
entry_points=[CommandHandler('list', self.entry_point)],
|
||||||
def create_handler(self):
|
states={
|
||||||
conv_handler = ConversationHandler(
|
NAME: [
|
||||||
entry_points=[CommandHandler('list', self.entry_point)],
|
CallbackQueryHandler(self.choose_list, pattern="^list-"),
|
||||||
states={
|
CallbackQueryHandler(self.new_list, pattern="^new$"),
|
||||||
NAME: [
|
],
|
||||||
CallbackQueryHandler(self.choose_list, pattern="^list-"),
|
NEW : [MessageHandler(Filters.text, callback=self.new_listname)],
|
||||||
CallbackQueryHandler(self.new_list, pattern="^new$"),
|
ACTION: [
|
||||||
],
|
CallbackQueryHandler(self.list_add, pattern="^add$"),
|
||||||
NEW : [MessageHandler(Filters.text, callback=self.new_listname)],
|
CallbackQueryHandler(self.list_remove, pattern="^remove$"),
|
||||||
ACTION: [
|
CallbackQueryHandler(self.list_clear, pattern="^clear$"),
|
||||||
CallbackQueryHandler(self.list_add, pattern="^add$"),
|
CallbackQueryHandler(self.list_delete, pattern="^delete$"),
|
||||||
CallbackQueryHandler(self.list_remove, pattern="^remove$"),
|
CallbackQueryHandler(self.list_print, pattern="^print$"),
|
||||||
CallbackQueryHandler(self.list_clear, pattern="^clear$"),
|
CallbackQueryHandler(self.list_menu, pattern="^overview$"),
|
||||||
CallbackQueryHandler(self.list_delete, pattern="^delete$"),
|
],
|
||||||
CallbackQueryHandler(self.list_print, pattern="^print$"),
|
ITEMADD : [MessageHandler(Filters.text, callback=self.list_add_item)],
|
||||||
CallbackQueryHandler(self.list_menu, pattern="^overview$"),
|
ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)]
|
||||||
],
|
},
|
||||||
ITEMADD : [MessageHandler(Filters.text, callback=self.list_add_item)],
|
fallbacks=[CommandHandler('list', self.entry_point)],
|
||||||
ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)]
|
)
|
||||||
},
|
return conv_handler
|
||||||
fallbacks=[CommandHandler('list', self.entry_point)],
|
|
||||||
)
|
|
||||||
return conv_handler
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
super().entry_point(update, context)
|
||||||
|
# TODO Change DB
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
lists = self.db.lists.select()
|
||||||
super().entry_point(update, context)
|
sl = [l.name for l in lists]
|
||||||
lists = self.db.lists.select()
|
keyboard = [[InlineKeyboardButton(k, callback_data="list-"+k)] for k in sl] + [[InlineKeyboardButton("New list", callback_data="new")]]
|
||||||
sl = [l.name for l in lists]
|
|
||||||
keyboard = [[InlineKeyboardButton(k, callback_data="list-"+k)] for k in sl] + [[InlineKeyboardButton("New list", callback_data="new")]]
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
super().log_activity(read=True, execute=False, send=True)
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup)
|
||||||
super().log_activity(read=True, execute=False, send=True)
|
return NAME
|
||||||
update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup)
|
|
||||||
return NAME
|
|
||||||
|
def choose_list(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def choose_list(self, update: Update, context: CallbackContext) -> None:
|
data = query.data
|
||||||
query = update.callback_query
|
name = data.replace("list-","")
|
||||||
data = query.data
|
query.answer()
|
||||||
name = data.replace("list-","")
|
self.current_name = name
|
||||||
query.answer()
|
|
||||||
self.current_name = name
|
keyboard = [
|
||||||
|
[InlineKeyboardButton("Add item", callback_data="add")],
|
||||||
keyboard = [
|
[InlineKeyboardButton("Remove item", callback_data="remove")],
|
||||||
[InlineKeyboardButton("Add item", callback_data="add")],
|
[InlineKeyboardButton("Clear list", callback_data="clear")],
|
||||||
[InlineKeyboardButton("Remove item", callback_data="remove")],
|
[InlineKeyboardButton("Print list", callback_data="print")],
|
||||||
[InlineKeyboardButton("Clear list", callback_data="clear")],
|
[InlineKeyboardButton("Delete list", callback_data="delete")],
|
||||||
[InlineKeyboardButton("Print list", callback_data="print")],
|
]
|
||||||
[InlineKeyboardButton("Delete list", callback_data="delete")],
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup)
|
||||||
|
return ACTION
|
||||||
query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
def list_menu(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_menu(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
|
||||||
query.answer()
|
keyboard = [
|
||||||
|
[InlineKeyboardButton("Add item", callback_data="add")],
|
||||||
keyboard = [
|
[InlineKeyboardButton("Remove item", callback_data="remove")],
|
||||||
[InlineKeyboardButton("Add item", callback_data="add")],
|
[InlineKeyboardButton("Clear list", callback_data="clear")],
|
||||||
[InlineKeyboardButton("Remove item", callback_data="remove")],
|
[InlineKeyboardButton("Print list", callback_data="print")],
|
||||||
[InlineKeyboardButton("Clear list", callback_data="clear")],
|
[InlineKeyboardButton("Delete list", callback_data="delete")],
|
||||||
[InlineKeyboardButton("Print list", callback_data="print")],
|
]
|
||||||
[InlineKeyboardButton("Delete list", callback_data="delete")],
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
query.edit_message_text("Very well. For " + self.current_name + " the following actions are available:", reply_markup=reply_markup)
|
||||||
|
return ACTION
|
||||||
query.edit_message_text("Very well. For " + self.current_name + " the following actions are available:", reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
def new_list(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def new_list(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
query.edit_message_text("What's the name of the new list?")
|
||||||
query.answer()
|
return NEW
|
||||||
query.edit_message_text("What's the name of the new list?")
|
|
||||||
return NEW
|
|
||||||
|
def new_listname(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
name = update.message.text
|
||||||
def new_listname(self, update: Update, context: CallbackContext) -> None:
|
try:
|
||||||
name = update.message.text
|
data = self.db.lists(name=name, content="")
|
||||||
try:
|
data.save()
|
||||||
data = self.db.lists(name=name, content="")
|
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]]
|
||||||
data.save()
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]]
|
self.current_name = name
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup)
|
||||||
self.current_name = name
|
return ACTION
|
||||||
update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup)
|
except Exception as e:
|
||||||
return ACTION
|
update.message.reply_text("Oh no! Encountered exception: {}".format(e))
|
||||||
except Exception as e:
|
return ConversationHandler.END
|
||||||
update.message.reply_text("Oh no! Encountered exception: {}".format(e))
|
|
||||||
return ConversationHandler.END
|
|
||||||
|
def list_add(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_add(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
query.edit_message_text("What would you like to add?")
|
||||||
query.answer()
|
return ITEMADD
|
||||||
query.edit_message_text("What would you like to add?")
|
|
||||||
return ITEMADD
|
|
||||||
|
def list_remove(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_remove(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
||||||
query.answer()
|
sl = it.content.split("<-->")
|
||||||
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
|
||||||
sl = it.content.split("<-->")
|
keyboard = [[InlineKeyboardButton(k, callback_data=i)] for i,k in enumerate(sl)]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton(k, callback_data=i)] for i,k in enumerate(sl)]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup)
|
||||||
|
return ITEMREMOVE
|
||||||
query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup)
|
|
||||||
return ITEMREMOVE
|
|
||||||
|
def list_clear(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_clear(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
self.db.lists.update(content="").where(self.db.lists.name == self.current_name).execute()
|
||||||
query.answer()
|
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
||||||
self.db.lists.update(content="").where(self.db.lists.name == self.current_name).execute()
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
query.edit_message_text("List " + self.current_name + " cleared", reply_markup=reply_markup)
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
return ACTION
|
||||||
query.edit_message_text("List " + self.current_name + " cleared", reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
def list_delete(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_delete(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
self.db.lists.delete().where(self.db.lists.name == self.current_name).execute()
|
||||||
query.answer()
|
query.edit_message_text("List " + self.current_name + " deleted")
|
||||||
self.db.lists.delete().where(self.db.lists.name == self.current_name).execute()
|
return ConversationHandler.END
|
||||||
query.edit_message_text("List " + self.current_name + " deleted")
|
|
||||||
return ConversationHandler.END
|
|
||||||
|
def list_print(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_print(self, update: Update, context: CallbackContext) -> None:
|
query.answer()
|
||||||
query = update.callback_query
|
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
||||||
query.answer()
|
if it:
|
||||||
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
content = it.content.split("<-->")
|
||||||
if it:
|
content = "\n".join(content)
|
||||||
content = it.content.split("<-->")
|
else:
|
||||||
content = "\n".join(content)
|
content = "List empty"
|
||||||
else:
|
|
||||||
content = "List empty"
|
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
query.edit_message_text("Content of " + self.current_name + ":\n" + content, reply_markup=reply_markup)
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
return ACTION
|
||||||
query.edit_message_text("Content of " + self.current_name + ":\n" + content, reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
def list_add_item(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
item = update.message.text
|
||||||
def list_add_item(self, update: Update, context: CallbackContext) -> None:
|
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
||||||
item = update.message.text
|
if it:
|
||||||
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
sl = it.content
|
||||||
if it:
|
else:
|
||||||
sl = it.content
|
sl = ""
|
||||||
else:
|
sl += item + "<-->"
|
||||||
sl = ""
|
self.db.lists.update(content=sl).where(self.db.lists.name == self.current_name).execute()
|
||||||
sl += item + "<-->"
|
|
||||||
self.db.lists.update(content=sl).where(self.db.lists.name == self.current_name).execute()
|
keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
update.message.reply_text("Added " + item, reply_markup=reply_markup)
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
return ACTION
|
||||||
update.message.reply_text("Added " + item, reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
def list_remove_index(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
query = update.callback_query
|
||||||
def list_remove_index(self, update: Update, context: CallbackContext) -> None:
|
ind = int(query.data)
|
||||||
query = update.callback_query
|
query.answer()
|
||||||
ind = int(query.data)
|
|
||||||
query.answer()
|
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
||||||
|
old = it.content.split("<-->")
|
||||||
it = self.db.lists.get(self.db.lists.name == self.current_name)
|
# todo make better
|
||||||
old = it.content.split("<-->")
|
|
||||||
# todo make better
|
name = old.pop(ind)
|
||||||
|
new = "<-->".join(old)
|
||||||
name = old.pop(ind)
|
self.db.lists.update(content=new).where(self.db.lists.name == self.current_name).execute()
|
||||||
new = "<-->".join(old)
|
|
||||||
self.db.lists.update(content=new).where(self.db.lists.name == self.current_name).execute()
|
keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
query.edit_message_text("Removed " + name, reply_markup=reply_markup)
|
||||||
|
return ACTION
|
||||||
query.edit_message_text("Removed " + name, reply_markup=reply_markup)
|
|
||||||
return ACTION
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
|
|
||||||
class Plain(BotFunc):
|
class Plain(BotFunc):
|
||||||
"""Not a command: just keeps logs and usage_data"""
|
"""Not a command: just keeps logs and usage_data"""
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
h = MessageHandler(Filters.text, callback=self.add_to_log)
|
h = MessageHandler(Filters.text, callback=self.add_to_log)
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def add_to_log(self, update: Update, context: CallbackContext) -> None:
|
def add_to_log(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
super().log_activity(
|
super().log_activity(
|
||||||
read = True,
|
read = True,
|
||||||
send = False,
|
send = False,
|
||||||
execute = False
|
execute = False
|
||||||
)
|
)
|
||||||
|
@ -1,179 +1,179 @@
|
|||||||
from re import U
|
from re import U
|
||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
|
|
||||||
CHOOSE_NUM = 1
|
CHOOSE_NUM = 1
|
||||||
class Joke(BotFunc):
|
class Joke(BotFunc):
|
||||||
"""Tells a joke from reddit."""
|
"""Tells a joke from reddit."""
|
||||||
|
|
||||||
def __init__(self, api, db):
|
def __init__(self, api, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.available_commands = {}
|
self.available_commands = {}
|
||||||
self.api = api
|
self.api = api
|
||||||
|
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('joke', self.entry_point)],
|
entry_points=[CommandHandler('joke', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
CHOOSE_NUM: [CallbackQueryHandler(self.get_jokes),],
|
CHOOSE_NUM: [CallbackQueryHandler(self.get_jokes),],
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('joke', self.entry_point)],
|
fallbacks=[CommandHandler('joke', self.entry_point)],
|
||||||
# conversation_timeout=5,
|
# conversation_timeout=5,
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
keyboard = [[InlineKeyboardButton(str(i), callback_data=str(i)) for i in range(1,11)]]
|
keyboard = [[InlineKeyboardButton(str(i), callback_data=str(i)) for i in range(1,11)]]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
||||||
update.message.reply_text("How many jokes?", reply_markup=reply_markup)
|
update.message.reply_text("How many jokes?", reply_markup=reply_markup)
|
||||||
return CHOOSE_NUM
|
return CHOOSE_NUM
|
||||||
|
|
||||||
|
|
||||||
def get_jokes(self, update: Update, context: CallbackContext) -> None:
|
def get_jokes(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
number = int(query.data)
|
number = int(query.data)
|
||||||
query.answer()
|
query.answer()
|
||||||
jokes = self.api.get_random_rising("jokes", number, "text")
|
jokes = self.api.get_random_rising("jokes", number, "text")
|
||||||
# formating
|
# formating
|
||||||
message = ""
|
message = ""
|
||||||
for j in jokes:
|
for j in jokes:
|
||||||
message += "<b>" + j["title"] + "</b> \n" + j["content"] + "\n\n"
|
message += "<b>" + j["title"] + "</b> \n" + j["content"] + "\n\n"
|
||||||
if message == "":
|
if message == "":
|
||||||
message += "Could not fetch jokes."
|
message += "Could not fetch jokes."
|
||||||
query.edit_message_text(text = message, parse_mode = ParseMode.HTML)
|
query.edit_message_text(text = message, parse_mode = ParseMode.HTML)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CHOOSE_TOPIC = 0
|
CHOOSE_TOPIC = 0
|
||||||
class Meme(BotFunc):
|
class Meme(BotFunc):
|
||||||
"""Gets the latest memes from reddit"""
|
"""Gets the latest memes from reddit"""
|
||||||
|
|
||||||
def __init__(self, api, db):
|
def __init__(self, api, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.available_commands = {}
|
self.available_commands = {}
|
||||||
self.api = api
|
self.api = api
|
||||||
|
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('meme', self.entry_point)],
|
entry_points=[CommandHandler('meme', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
CHOOSE_TOPIC: [CallbackQueryHandler(self.choose_topic)],
|
CHOOSE_TOPIC: [CallbackQueryHandler(self.choose_topic)],
|
||||||
CHOOSE_NUM :[CallbackQueryHandler(self.get_memes)],
|
CHOOSE_NUM :[CallbackQueryHandler(self.get_memes)],
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('meme', self.entry_point)],
|
fallbacks=[CommandHandler('meme', self.entry_point)],
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[InlineKeyboardButton("General", callback_data="memes"),],
|
[InlineKeyboardButton("General", callback_data="memes"),],
|
||||||
[InlineKeyboardButton("Dank memes", callback_data="dankmemes"),],
|
[InlineKeyboardButton("Dank memes", callback_data="dankmemes"),],
|
||||||
[InlineKeyboardButton("Maths", callback_data="mathmemes"),],
|
[InlineKeyboardButton("Maths", callback_data="mathmemes"),],
|
||||||
[InlineKeyboardButton("Physics", callback_data="physicsmemes"),],
|
[InlineKeyboardButton("Physics", callback_data="physicsmemes"),],
|
||||||
[InlineKeyboardButton("Biology", callback_data="biologymemes"),],
|
[InlineKeyboardButton("Biology", callback_data="biologymemes"),],
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
|
||||||
update.message.reply_text("What kind of memes?", reply_markup=reply_markup)
|
update.message.reply_text("What kind of memes?", reply_markup=reply_markup)
|
||||||
return CHOOSE_TOPIC
|
return CHOOSE_TOPIC
|
||||||
|
|
||||||
|
|
||||||
def choose_topic(self, update: Update, context: CallbackContext) -> None:
|
def choose_topic(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
d = query.data
|
d = query.data
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
keyboard = [[InlineKeyboardButton(str(i), callback_data=d + "-" + str(i)) for i in range(1,11)]]
|
keyboard = [[InlineKeyboardButton(str(i), callback_data=d + "-" + str(i)) for i in range(1,11)]]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
query.edit_message_text("How many memes?", reply_markup=reply_markup)
|
query.edit_message_text("How many memes?", reply_markup=reply_markup)
|
||||||
return CHOOSE_NUM
|
return CHOOSE_NUM
|
||||||
|
|
||||||
|
|
||||||
def get_memes(self, update: Update, context: CallbackContext) -> None:
|
def get_memes(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data.split("-")
|
data = query.data.split("-")
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
memes = self.api.get_random_rising(data[0], int(data[1]), "photo")
|
memes = self.api.get_random_rising(data[0], int(data[1]), "photo")
|
||||||
if len(memes) != 0:
|
if len(memes) != 0:
|
||||||
for m in memes:
|
for m in memes:
|
||||||
super().log_activity(read=False, execute=False, send=True) # we just sent an additional message
|
super().log_activity(read=False, execute=False, send=True) # we just sent an additional message
|
||||||
update.effective_chat.send_photo(photo = m["image"],caption = m["caption"])
|
update.effective_chat.send_photo(photo = m["image"],caption = m["caption"])
|
||||||
else:
|
else:
|
||||||
update.effective_chat.send_message("Sorry, the meme won't yeet.")
|
update.effective_chat.send_message("Sorry, the meme won't yeet.")
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# class News(BotFunc):
|
# class News(BotFunc):
|
||||||
# """Gets the latest news from reddit"""
|
# """Gets the latest news from reddit"""
|
||||||
|
|
||||||
# def __init__(self, api, prst):
|
# def __init__(self, api, prst):
|
||||||
# super().__init__(prst)
|
# super().__init__(prst)
|
||||||
# self.available_commands = {}
|
# self.available_commands = {}
|
||||||
# self.api = api
|
# self.api = api
|
||||||
|
|
||||||
|
|
||||||
# def create_handler(self):
|
# def create_handler(self):
|
||||||
# conv_handler = ConversationHandler(
|
# conv_handler = ConversationHandler(
|
||||||
# entry_points=[CommandHandler('news', self.entry_point)],
|
# entry_points=[CommandHandler('news', self.entry_point)],
|
||||||
# states={
|
# states={
|
||||||
# CHOOSE_TOPIC: [CallbackQueryHandler(self.choose_topic)],
|
# CHOOSE_TOPIC: [CallbackQueryHandler(self.choose_topic)],
|
||||||
# CHOOSE_NUM :[CallbackQueryHandler(self.get_news)],
|
# CHOOSE_NUM :[CallbackQueryHandler(self.get_news)],
|
||||||
# },
|
# },
|
||||||
# fallbacks=[CommandHandler('news', self.entry_point)],
|
# fallbacks=[CommandHandler('news', self.entry_point)],
|
||||||
# )
|
# )
|
||||||
# return conv_handler
|
# return conv_handler
|
||||||
|
|
||||||
|
|
||||||
# def entry_point(self, update: Update, context: CallbackContext) -> None:
|
# def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
# super().entry_point()
|
# super().entry_point()
|
||||||
|
|
||||||
# keyboard = [
|
# keyboard = [
|
||||||
# [InlineKeyboardButton("World", callback_data="worldnews"),],
|
# [InlineKeyboardButton("World", callback_data="worldnews"),],
|
||||||
# [InlineKeyboardButton("Germany", callback_data="germannews"),],
|
# [InlineKeyboardButton("Germany", callback_data="germannews"),],
|
||||||
# [InlineKeyboardButton("France", callback_data="francenews"),],
|
# [InlineKeyboardButton("France", callback_data="francenews"),],
|
||||||
# [InlineKeyboardButton("Europe", callback_data="eunews"),],
|
# [InlineKeyboardButton("Europe", callback_data="eunews"),],
|
||||||
# [InlineKeyboardButton("USA", callback_data="usanews"),],
|
# [InlineKeyboardButton("USA", callback_data="usanews"),],
|
||||||
# ]
|
# ]
|
||||||
# reply_markup = InlineKeyboardMarkup(keyboard)
|
# reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
# update.message.reply_text("What kind of news?", reply_markup=reply_markup)
|
# update.message.reply_text("What kind of news?", reply_markup=reply_markup)
|
||||||
# return CHOOSE_TOPIC
|
# return CHOOSE_TOPIC
|
||||||
|
|
||||||
|
|
||||||
# def choose_topic(self, update: Update, context: CallbackContext) -> None:
|
# def choose_topic(self, update: Update, context: CallbackContext) -> None:
|
||||||
# super().entry_point()
|
# super().entry_point()
|
||||||
# query = update.callback_query
|
# query = update.callback_query
|
||||||
# d = query.data
|
# d = query.data
|
||||||
# query.answer()
|
# query.answer()
|
||||||
|
|
||||||
# keyboard = [[InlineKeyboardButton(str(i), callback_data=d + "-" + str(i)) for i in range(1,11)]]
|
# keyboard = [[InlineKeyboardButton(str(i), callback_data=d + "-" + str(i)) for i in range(1,11)]]
|
||||||
# reply_markup = InlineKeyboardMarkup(keyboard)
|
# reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
# query.edit_message_text("How many entries?", reply_markup=reply_markup)
|
# query.edit_message_text("How many entries?", reply_markup=reply_markup)
|
||||||
# return CHOOSE_NUM
|
# return CHOOSE_NUM
|
||||||
|
|
||||||
|
|
||||||
# def get_news(self, update: Update, context: CallbackContext) -> None:
|
# def get_news(self, update: Update, context: CallbackContext) -> None:
|
||||||
# query = update.callback_query
|
# query = update.callback_query
|
||||||
# data = query.data.split("-")
|
# data = query.data.split("-")
|
||||||
# query.answer()
|
# query.answer()
|
||||||
# #try:
|
# #try:
|
||||||
# news = self.api.get_top(data[0], data[1], "text")
|
# news = self.api.get_top(data[0], data[1], "text")
|
||||||
# # formating
|
# # formating
|
||||||
# message = ""
|
# message = ""
|
||||||
# for j in news:
|
# for j in news:
|
||||||
# message += "<b>" + j["title"] + "</b> \n" + j["content"] + "\n\n"
|
# message += "<b>" + j["title"] + "</b> \n" + j["content"] + "\n\n"
|
||||||
# if message == "":
|
# if message == "":
|
||||||
# message += "Could not fetch news."
|
# message += "Could not fetch news."
|
||||||
# query.edit_message_text(news, paresemode=ParseMode.HTML)
|
# query.edit_message_text(news, paresemode=ParseMode.HTML)
|
||||||
# return ConversationHandler.END
|
# return ConversationHandler.END
|
@ -1,61 +1,61 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
|
|
||||||
SEARCH, MORE = range(2)
|
SEARCH, MORE = range(2)
|
||||||
class Search(BotFunc):
|
class Search(BotFunc):
|
||||||
"""Browse the web for a topic."""
|
"""Browse the web for a topic."""
|
||||||
|
|
||||||
def __init__(self, api, db):
|
def __init__(self, api, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.available_commands = {}
|
self.available_commands = {}
|
||||||
self.api = api
|
self.api = api
|
||||||
|
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('search', self.entry_point)],
|
entry_points=[CommandHandler('search', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
SEARCH: [MessageHandler(Filters.text, self.get_results),],
|
SEARCH: [MessageHandler(Filters.text, self.get_results),],
|
||||||
MORE: [CallbackQueryHandler(self.show_more, pattern="^more$"),],
|
MORE: [CallbackQueryHandler(self.show_more, pattern="^more$"),],
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('search', self.entry_point)],
|
fallbacks=[CommandHandler('search', self.entry_point)],
|
||||||
conversation_timeout=20,
|
conversation_timeout=20,
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
update.message.reply_text("What are we searching?")
|
update.message.reply_text("What are we searching?")
|
||||||
return SEARCH
|
return SEARCH
|
||||||
|
|
||||||
|
|
||||||
def get_results(self, update: Update, context: CallbackContext) -> None:
|
def get_results(self, update: Update, context: CallbackContext) -> None:
|
||||||
search = update.message.text
|
search = update.message.text
|
||||||
results = self.api.get_result(search)
|
results = self.api.get_result(search)
|
||||||
keyboard = [[InlineKeyboardButton("More!", callback_data="more")]]
|
keyboard = [[InlineKeyboardButton("More!", callback_data="more")]]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
# formating
|
# formating
|
||||||
self.results = results
|
self.results = results
|
||||||
if results:
|
if results:
|
||||||
first = results[0]
|
first = results[0]
|
||||||
message = first["text"] + "\n(" + first["url"] + ")\n\n"
|
message = first["text"] + "\n(" + first["url"] + ")\n\n"
|
||||||
else:
|
else:
|
||||||
message = "No results for search query."
|
message = "No results for search query."
|
||||||
update.message.reply_text(text = message, reply_markup=reply_markup)
|
update.message.reply_text(text = message, reply_markup=reply_markup)
|
||||||
super().log_activity(read = True, execute = True, send = True)
|
super().log_activity(read = True, execute = True, send = True)
|
||||||
return MORE
|
return MORE
|
||||||
|
|
||||||
|
|
||||||
def show_more(self, update: Update, context: CallbackContext) -> None:
|
def show_more(self, update: Update, context: CallbackContext) -> None:
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
|
|
||||||
message = ""
|
message = ""
|
||||||
for r in self.results:
|
for r in self.results:
|
||||||
message += r["text"] + "\n(" + r["url"] + ")\n\n"
|
message += r["text"] + "\n(" + r["url"] + ")\n\n"
|
||||||
|
|
||||||
query.edit_message_text(message)
|
query.edit_message_text(message)
|
||||||
super().log_activity(read = False, execute = False, send = True)
|
super().log_activity(read = False, execute = False, send = True)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
@ -1,105 +1,106 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
import socket
|
import socket
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
FIRST = 1
|
FIRST = 1
|
||||||
|
|
||||||
class Status(BotFunc):
|
class Status(BotFunc):
|
||||||
"""Shows a short status of the program."""
|
"""Shows a short status of the program."""
|
||||||
|
|
||||||
def __init__(self, name, version, db):
|
def __init__(self, name, version, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.start_time = datetime.datetime.now()
|
self.start_time = datetime.datetime.now()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('status', self.entry_point)],
|
entry_points=[CommandHandler('status', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
FIRST: [
|
FIRST: [
|
||||||
CallbackQueryHandler(self.send_log, pattern="^full$"),
|
CallbackQueryHandler(self.send_log, pattern="^full$"),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('status', self.entry_point)],
|
fallbacks=[CommandHandler('status', self.entry_point)],
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("And the log?", callback_data="full"),
|
InlineKeyboardButton("And the log?", callback_data="full"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
delta = str(datetime.datetime.now() - self.start_time)
|
delta = str(datetime.datetime.now() - self.start_time)
|
||||||
message = "BeebBop, this is " + self.name + " (V." + self.version + ")\n"
|
message = "BeebBop, this is " + self.name + " (V." + self.version + ")\n"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ip = requests.get('https://api.ipify.org').text
|
ip = requests.get('https://api.ipify.org').text
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||||
s.connect(('8.8.8.8', 80))
|
s.connect(('8.8.8.8', 80))
|
||||||
(addr, port) = s.getsockname()
|
(addr, port) = s.getsockname()
|
||||||
local_ips = addr
|
local_ips = addr
|
||||||
except:
|
except:
|
||||||
ip = "not fetchable"
|
ip = "not fetchable"
|
||||||
local_ips = "not fetchable"
|
local_ips = "not fetchable"
|
||||||
|
|
||||||
message += "Status: Running 🟢\n"
|
message += "Status: Running 🟢\n"
|
||||||
message += "Uptime: `" + delta[:delta.rfind(".")] + "`\n"
|
message += "Uptime: `" + delta[:delta.rfind(".")] + "`\n"
|
||||||
# message += "Reboots: `" + str(self.persistence["global"]["reboots"]) + "`\n"
|
# message += "Reboots: `" + str(self.persistence["global"]["reboots"]) + "`\n"
|
||||||
message += "IP (public): `" + ip + "`\n"
|
message += "IP (public): `" + ip + "`\n"
|
||||||
message += "IP (private): `" + str(local_ips) + "`\n"
|
message += "IP (private): `" + str(local_ips) + "`\n"
|
||||||
u = str(self.get_ngrok_url())
|
u = str(self.get_ngrok_url())
|
||||||
message += "URL: [" + u + "](" + u + ")\n"
|
message += "URL: [" + u + "](" + u + ")\n"
|
||||||
|
|
||||||
tot_r = self.db.chats.select().where(self.db.chats.read == True).count()
|
# TODO new DB
|
||||||
message += "Total messages read: `{}`\n".format(tot_r)
|
tot_r = self.db.chats.select().where(self.db.chats.read == True).count()
|
||||||
|
message += "Total messages read: `{}`\n".format(tot_r)
|
||||||
tot_s = self.db.chats.select().where(self.db.chats.send == True).count()
|
|
||||||
message += "Total messages sent: `{}`\n".format(tot_s)
|
tot_s = self.db.chats.select().where(self.db.chats.send == True).count()
|
||||||
|
message += "Total messages sent: `{}`\n".format(tot_s)
|
||||||
tot_e = self.db.chats.select().where(self.db.chats.execute == True).count()
|
|
||||||
message += "Total commands executed: `{}`\n".format(tot_e)
|
tot_e = self.db.chats.select().where(self.db.chats.execute == True).count()
|
||||||
|
message += "Total commands executed: `{}`\n".format(tot_e)
|
||||||
if update.message:
|
|
||||||
update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
|
if update.message:
|
||||||
else:
|
update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
|
||||||
update._effective_chat.send_message(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
|
else:
|
||||||
|
update._effective_chat.send_message(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
|
||||||
super().log_activity(read = True, execute = True, send = True)
|
|
||||||
return FIRST
|
super().log_activity(read = True, execute = True, send = True)
|
||||||
|
return FIRST
|
||||||
|
|
||||||
def send_log(self, update: Update, context: CallbackContext) -> None:
|
|
||||||
query = update.callback_query
|
def send_log(self, update: Update, context: CallbackContext) -> None:
|
||||||
wanted = query.data.replace("status-","")
|
query = update.callback_query
|
||||||
query.answer()
|
wanted = query.data.replace("status-","")
|
||||||
with open("persistence/server.log") as l:
|
query.answer()
|
||||||
query.message.reply_document(l)
|
with open("persistence/server.log") as l:
|
||||||
|
query.message.reply_document(l)
|
||||||
super().log_activity(read = False, execute = False, send = True)
|
|
||||||
return ConversationHandler.END
|
super().log_activity(read = False, execute = False, send = True)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
def get_ngrok_url(self):
|
|
||||||
try:
|
def get_ngrok_url(self):
|
||||||
url = "http://localhost:4040/api/tunnels/"
|
try:
|
||||||
res = requests.get(url)
|
url = "http://localhost:4040/api/tunnels/"
|
||||||
res_unicode = res.content.decode("utf-8")
|
res = requests.get(url)
|
||||||
res_json = json.loads(res_unicode)
|
res_unicode = res.content.decode("utf-8")
|
||||||
for i in res_json["tunnels"]:
|
res_json = json.loads(res_unicode)
|
||||||
if i['name'] == 'command_line':
|
for i in res_json["tunnels"]:
|
||||||
return i['public_url']
|
if i['name'] == 'command_line':
|
||||||
break
|
return i['public_url']
|
||||||
except:
|
break
|
||||||
|
except:
|
||||||
return "Not available"
|
return "Not available"
|
@ -1,44 +1,43 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode
|
||||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode
|
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters
|
||||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters
|
from telegram.ext import (
|
||||||
from telegram.ext import (
|
Updater,
|
||||||
Updater,
|
CommandHandler,
|
||||||
CommandHandler,
|
CallbackQueryHandler,
|
||||||
CallbackQueryHandler,
|
ConversationHandler,
|
||||||
ConversationHandler,
|
CallbackContext,
|
||||||
CallbackContext,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
import datetime
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
class BotFunc():
|
||||||
class BotFunc():
|
"""Base class for a specific bot-functionality"""
|
||||||
"""Base class for a specific bot-functionality"""
|
def __init__(self, db):
|
||||||
def __init__(self, db):
|
self.logger = logging.getLogger(__name__)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.db = db
|
||||||
self.db = db
|
|
||||||
|
|
||||||
|
# def log_activity(self, **kwargs):
|
||||||
def log_activity(self, **kwargs):
|
# # mark that a new command has been executed
|
||||||
# mark that a new command has been executed
|
# try:
|
||||||
try:
|
# data = self.db.chats(
|
||||||
data = self.db.chats(
|
# time=datetime.datetime.now(),
|
||||||
time=datetime.datetime.now(),
|
# **kwargs
|
||||||
**kwargs
|
# )
|
||||||
)
|
# # kwargs can look like
|
||||||
# kwargs can look like
|
# # receive=True,
|
||||||
# receive=True,
|
# # execute=True,
|
||||||
# execute=True,
|
# # send=False,
|
||||||
# send=False,
|
# data.save()
|
||||||
data.save()
|
# except Exception as e:
|
||||||
except Exception as e:
|
# self.logger.error("sql error: {}".format(e))
|
||||||
self.logger.error("sql error: {}".format(e))
|
|
||||||
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
if update.message.text:
|
||||||
if update.message.text:
|
self.logger.info("Chat said: {}".format(update.message.text))
|
||||||
self.logger.info("Chat said: {}".format(update.message.text))
|
else:
|
||||||
else:
|
self.logger.info("Chat said: {}".format(update.message))
|
||||||
self.logger.info("Chat said: {}".format(update.message))
|
|
||||||
|
|
||||||
|
@ -1,117 +1,117 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
FIRST = 1
|
FIRST = 1
|
||||||
|
|
||||||
class Weather(BotFunc):
|
class Weather(BotFunc):
|
||||||
"""Shows a weatherforecast for a given location"""
|
"""Shows a weatherforecast for a given location"""
|
||||||
def __init__(self, api, db):
|
def __init__(self, api, db):
|
||||||
"""initialize api and persistence"""
|
"""initialize api and persistence"""
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.api = api
|
self.api = api
|
||||||
self.city = ""
|
self.city = ""
|
||||||
|
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
"""returns the handlers with button-logic"""
|
"""returns the handlers with button-logic"""
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('weather', self.entry_point)],
|
entry_points=[CommandHandler('weather', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
FIRST: [
|
FIRST: [
|
||||||
CallbackQueryHandler(self.choose_city, pattern="^city-"),
|
CallbackQueryHandler(self.choose_city, pattern="^city-"),
|
||||||
CallbackQueryHandler(self.choose_time, pattern="^time-"),
|
CallbackQueryHandler(self.choose_time, pattern="^time-"),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('weather', self.entry_point)],
|
fallbacks=[CommandHandler('weather', self.entry_point)],
|
||||||
)
|
)
|
||||||
|
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
"""Reacts the call of the command. Prints the first buttons"""
|
"""Reacts the call of the command. Prints the first buttons"""
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("Zürich", callback_data="city-zurich"),
|
InlineKeyboardButton("Zürich", callback_data="city-zurich"),
|
||||||
InlineKeyboardButton("Freiburg", callback_data="city-freiburg"),
|
InlineKeyboardButton("Freiburg", callback_data="city-freiburg"),
|
||||||
InlineKeyboardButton("Mulhouse", callback_data="city-mulhouse"),
|
InlineKeyboardButton("Mulhouse", callback_data="city-mulhouse"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
if update.message:
|
if update.message:
|
||||||
update.message.reply_text("Which city?", reply_markup=reply_markup)
|
update.message.reply_text("Which city?", reply_markup=reply_markup)
|
||||||
else:
|
else:
|
||||||
update.callback_query.edit_message_text("Which city", reply_markup=reply_markup)
|
update.callback_query.edit_message_text("Which city", reply_markup=reply_markup)
|
||||||
return FIRST
|
return FIRST
|
||||||
|
|
||||||
|
|
||||||
def choose_city(self, update: Update, context: CallbackContext) -> None:
|
def choose_city(self, update: Update, context: CallbackContext) -> None:
|
||||||
"""Prompt same text & keyboard as `start` does but not as new message"""
|
"""Prompt same text & keyboard as `start` does but not as new message"""
|
||||||
# Get CallbackQuery from Update
|
# Get CallbackQuery from Update
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data
|
data = query.data
|
||||||
self.city = data.replace("city-","")
|
self.city = data.replace("city-","")
|
||||||
query.answer()
|
query.answer()
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("Now", callback_data="time-now"),
|
InlineKeyboardButton("Now", callback_data="time-now"),
|
||||||
InlineKeyboardButton("Tomorrow", callback_data="time-tomorrow"),
|
InlineKeyboardButton("Tomorrow", callback_data="time-tomorrow"),
|
||||||
InlineKeyboardButton("7 days", callback_data="time-7"),
|
InlineKeyboardButton("7 days", callback_data="time-7"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
query.edit_message_text(
|
query.edit_message_text(
|
||||||
text = "Which time?", reply_markup=reply_markup
|
text = "Which time?", reply_markup=reply_markup
|
||||||
)
|
)
|
||||||
return FIRST
|
return FIRST
|
||||||
|
|
||||||
|
|
||||||
def choose_time(self, update: Update, context: CallbackContext) -> None:
|
def choose_time(self, update: Update, context: CallbackContext) -> None:
|
||||||
"""Show new choice of buttons"""
|
"""Show new choice of buttons"""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
query.answer()
|
query.answer()
|
||||||
forecast_time = query.data.replace("time-","")
|
forecast_time = query.data.replace("time-","")
|
||||||
weather = self.get_weather(self.city, forecast_time)
|
weather = self.get_weather(self.city, forecast_time)
|
||||||
query.edit_message_text(
|
query.edit_message_text(
|
||||||
text = "Weather: \n\n" + weather,
|
text = "Weather: \n\n" + weather,
|
||||||
parse_mode = ParseMode.HTML
|
parse_mode = ParseMode.HTML
|
||||||
)
|
)
|
||||||
super().log_activity(read = True, execute = True, send = True)
|
super().log_activity(read = True, execute = True, send = True)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def get_weather(self, city, forecast_time) -> None:
|
def get_weather(self, city, forecast_time) -> None:
|
||||||
"""get the weather that matches the given params"""
|
"""get the weather that matches the given params"""
|
||||||
locations = {"freiburg": [47.9990, 7.8421], "zurich": [47.3769, 8.5417], "mulhouse": [47.7508, 7.3359]}
|
locations = {"freiburg": [47.9990, 7.8421], "zurich": [47.3769, 8.5417], "mulhouse": [47.7508, 7.3359]}
|
||||||
|
|
||||||
city = locations[city]
|
city = locations[city]
|
||||||
|
|
||||||
categories = {"Clouds": "☁", "Rain": "🌧", "Thunderstorm": "🌩", "Drizzle": ":droplet:", "Snow": "❄", "Clear": "☀", "Mist": "🌫", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
categories = {"Clouds": "☁", "Rain": "🌧", "Thunderstorm": "🌩", "Drizzle": ":droplet:", "Snow": "❄", "Clear": "☀", "Mist": "🌫", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
||||||
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||||
today = datetime.datetime.today().weekday()
|
today = datetime.datetime.today().weekday()
|
||||||
weather = self.api.show_weather(city)
|
weather = self.api.show_weather(city)
|
||||||
message = ""
|
message = ""
|
||||||
if forecast_time == "now" or forecast_time == "7":
|
if forecast_time == "now" or forecast_time == "7":
|
||||||
now = weather.pop(0)
|
now = weather.pop(0)
|
||||||
message += "<b>Now:</b> " + categories[now["short"]] + "\n"
|
message += "<b>Now:</b> " + categories[now["short"]] + "\n"
|
||||||
message += "🌡" + str(now["temps"][0]) + "°\n\n"
|
message += "🌡" + str(now["temps"][0]) + "°\n\n"
|
||||||
tod = weather.pop(0)
|
tod = weather.pop(0)
|
||||||
message += "<b>" + "Today" + ":</b> " + categories[tod["short"]] + "\n"
|
message += "<b>" + "Today" + ":</b> " + categories[tod["short"]] + "\n"
|
||||||
message += "🌡 ❄ " + str(tod["temps"][0]) + "° , 🌡 🔥 " + str(tod["temps"][1]) + "°\n\n"
|
message += "🌡 ❄ " + str(tod["temps"][0]) + "° , 🌡 🔥 " + str(tod["temps"][1]) + "°\n\n"
|
||||||
|
|
||||||
if forecast_time == "tomorrow" or forecast_time == "7":
|
if forecast_time == "tomorrow" or forecast_time == "7":
|
||||||
if forecast_time == "tomorrow": # previous statement was not executed: tomorrow is at weather[2]
|
if forecast_time == "tomorrow": # previous statement was not executed: tomorrow is at weather[2]
|
||||||
tom = weather.pop(2)
|
tom = weather.pop(2)
|
||||||
else:
|
else:
|
||||||
tom = weather.pop(0)
|
tom = weather.pop(0)
|
||||||
message += "<b>" + "Tomorrow" + ":</b> " + categories[tom["short"]] + "\n"
|
message += "<b>" + "Tomorrow" + ":</b> " + categories[tom["short"]] + "\n"
|
||||||
message += "🌡 ❄ " + str(tom["temps"][0]) + "° , 🌡 🔥 " + str(tom["temps"][1]) + "°\n\n"
|
message += "🌡 ❄ " + str(tom["temps"][0]) + "° , 🌡 🔥 " + str(tom["temps"][1]) + "°\n\n"
|
||||||
|
|
||||||
if forecast_time == "7":
|
if forecast_time == "7":
|
||||||
for i, day in enumerate(weather):
|
for i, day in enumerate(weather):
|
||||||
message += "<b>" + days[(today + i + 2) % 7] + ":</b> " + categories[day["short"]] + "\n"
|
message += "<b>" + days[(today + i + 2) % 7] + ":</b> " + categories[day["short"]] + "\n"
|
||||||
message += "🌡 ❄ " + str(day["temps"][0]) + "° , 🌡 🔥 " + str(day["temps"][1]) + "°\n\n"
|
message += "🌡 ❄ " + str(day["temps"][0]) + "° , 🌡 🔥 " + str(day["temps"][1]) + "°\n\n"
|
||||||
|
|
||||||
return message
|
return message
|
||||||
|
@ -1,87 +1,87 @@
|
|||||||
from .template import *
|
from .template import *
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
START, DEST = range(2)
|
START, DEST = range(2)
|
||||||
|
|
||||||
class Zvv(BotFunc):
|
class Zvv(BotFunc):
|
||||||
"""Connects to the swiss travel-api to get public transport routes"""
|
"""Connects to the swiss travel-api to get public transport routes"""
|
||||||
|
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
super().__init__(db)
|
super().__init__(db)
|
||||||
self.start = ""
|
self.start = ""
|
||||||
self.dest = ""
|
self.dest = ""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create_handler(self):
|
def create_handler(self):
|
||||||
conv_handler = ConversationHandler(
|
conv_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler('zvv', self.entry_point)],
|
entry_points=[CommandHandler('zvv', self.entry_point)],
|
||||||
states={
|
states={
|
||||||
START: [MessageHandler(Filters.text, callback=self.get_start)],
|
START: [MessageHandler(Filters.text, callback=self.get_start)],
|
||||||
DEST: [MessageHandler(Filters.text, callback=self.get_dest)]
|
DEST: [MessageHandler(Filters.text, callback=self.get_dest)]
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler('zvv', self.entry_point)],
|
fallbacks=[CommandHandler('zvv', self.entry_point)],
|
||||||
)
|
)
|
||||||
return conv_handler
|
return conv_handler
|
||||||
|
|
||||||
|
|
||||||
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
def entry_point(self, update: Update, context: CallbackContext) -> None:
|
||||||
super().entry_point(update, context)
|
super().entry_point(update, context)
|
||||||
update.message.reply_text("What is the start point?")
|
update.message.reply_text("What is the start point?")
|
||||||
return START
|
return START
|
||||||
|
|
||||||
|
|
||||||
def get_start(self, update: Update, context: CallbackContext) -> None:
|
def get_start(self, update: Update, context: CallbackContext) -> None:
|
||||||
loc = update.message.text
|
loc = update.message.text
|
||||||
self.start = loc
|
self.start = loc
|
||||||
update.message.reply_text("Ok. Going from " + loc + ", what is the destination?")
|
update.message.reply_text("Ok. Going from " + loc + ", what is the destination?")
|
||||||
return DEST
|
return DEST
|
||||||
|
|
||||||
|
|
||||||
def get_dest(self, update: Update, context: CallbackContext) -> None:
|
def get_dest(self, update: Update, context: CallbackContext) -> None:
|
||||||
loc = update.message.text
|
loc = update.message.text
|
||||||
self.dest = loc
|
self.dest = loc
|
||||||
route = self.get_result()
|
route = self.get_result()
|
||||||
update.message.reply_text("Here are the routes I've got:\n" + route)
|
update.message.reply_text("Here are the routes I've got:\n" + route)
|
||||||
super().log_activity(read=True, execute=True, send=True)
|
super().log_activity(read=True, execute=True, send=True)
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def get_result(self):
|
def get_result(self):
|
||||||
url = "http://transport.opendata.ch/v1/connections"
|
url = "http://transport.opendata.ch/v1/connections"
|
||||||
|
|
||||||
start = self.start
|
start = self.start
|
||||||
dest = self.dest
|
dest = self.dest
|
||||||
|
|
||||||
data = {"from" : start, "to" : dest, "limit" : 2}
|
data = {"from" : start, "to" : dest, "limit" : 2}
|
||||||
try:
|
try:
|
||||||
routes = requests.get(url, params=data).json()
|
routes = requests.get(url, params=data).json()
|
||||||
result = routes["connections"]
|
result = routes["connections"]
|
||||||
text = result[0]["from"]["station"]["name"] + " ⏩ " + result[0]["to"]["station"]["name"] + "\n\n"
|
text = result[0]["from"]["station"]["name"] + " ⏩ " + result[0]["to"]["station"]["name"] + "\n\n"
|
||||||
for con in result:
|
for con in result:
|
||||||
text += "Start: " + datetime.datetime.fromtimestamp(int(con["from"]["departureTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
|
text += "Start: " + datetime.datetime.fromtimestamp(int(con["from"]["departureTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
|
||||||
text += "🏁 " + datetime.datetime.fromtimestamp(int(con["to"]["arrivalTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
|
text += "🏁 " + datetime.datetime.fromtimestamp(int(con["to"]["arrivalTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
|
||||||
text += "⏳ " + con["duration"] + "\n"
|
text += "⏳ " + con["duration"] + "\n"
|
||||||
text += "🗺️ Route:\n"
|
text += "🗺️ Route:\n"
|
||||||
|
|
||||||
for step in con["sections"]:
|
for step in con["sections"]:
|
||||||
if step["journey"] != None:
|
if step["journey"] != None:
|
||||||
text += step["journey"]["passList"][0]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][0]["departureTimestamp"])).strftime("%H:%M") + ")\n"
|
text += step["journey"]["passList"][0]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][0]["departureTimestamp"])).strftime("%H:%M") + ")\n"
|
||||||
|
|
||||||
text += "➡️ Linie " + self.number_to_emoji(step["journey"]["number"]) + "\n"
|
text += "➡️ Linie " + self.number_to_emoji(step["journey"]["number"]) + "\n"
|
||||||
|
|
||||||
text += step["journey"]["passList"][-1]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][-1]["arrivalTimestamp"])).strftime("%H:%M") +")\n"
|
text += step["journey"]["passList"][-1]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][-1]["arrivalTimestamp"])).strftime("%H:%M") +")\n"
|
||||||
else:
|
else:
|
||||||
text += "Walk."
|
text += "Walk."
|
||||||
text += "\n"
|
text += "\n"
|
||||||
return text
|
return text
|
||||||
except:
|
except:
|
||||||
return "Invalid api call."
|
return "Invalid api call."
|
||||||
|
|
||||||
def number_to_emoji(self, number):
|
def number_to_emoji(self, number):
|
||||||
out = ""
|
out = ""
|
||||||
numbers = ["0️⃣","1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"]
|
numbers = ["0️⃣","1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"]
|
||||||
for i in str(number):
|
for i in str(number):
|
||||||
out += numbers[int(i)]
|
out += numbers[int(i)]
|
||||||
return str(out)
|
return str(out)
|
136
bot/main.py
136
bot/main.py
@ -1,68 +1,68 @@
|
|||||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
|
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
|
||||||
|
|
||||||
from . import api, commands
|
from . import api, commands
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ChatBot():
|
class ChatBot():
|
||||||
"""better framwork - unites all functions"""
|
"""better framwork - unites all functions"""
|
||||||
|
|
||||||
def __init__(self, name, version):
|
def __init__(self, name, version):
|
||||||
"""Inits the Bot with a few conf. vars
|
"""Inits the Bot with a few conf. vars
|
||||||
Args: -> name:str - Name of the bot
|
Args: -> name:str - Name of the bot
|
||||||
-> version:str - Version number
|
-> version:str - Version number
|
||||||
-> hw_commands - dict with commands executable by the clock module
|
-> hw_commands - dict with commands executable by the clock module
|
||||||
-> prst:dict - persistence (overloaded dict that writes to json file)
|
-> prst:dict - persistence (overloaded dict that writes to json file)
|
||||||
-> logger - logging object to unify log messages
|
-> logger - logging object to unify log messages
|
||||||
"""
|
"""
|
||||||
# added by the launcher, we have self.modules (dict) and persistence and db
|
# added by the launcher, we have self.modules (dict) and persistence and db
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|
||||||
# Import submodules
|
# Import submodules
|
||||||
self.api_weather = api.weather.WeatherFetch(api.keys.weather_api)
|
self.api_weather = api.weather.WeatherFetch(api.keys.weather_api)
|
||||||
self.api_reddit = api.reddit.RedditFetch(api.keys.reddit_api)
|
self.api_reddit = api.reddit.RedditFetch(api.keys.reddit_api)
|
||||||
self.api_search = api.search.WebSearch()
|
self.api_search = api.search.WebSearch()
|
||||||
self.api_art = api.metmuseum.ArtFetch()
|
self.api_art = api.metmuseum.ArtFetch()
|
||||||
# and so on
|
# and so on
|
||||||
|
|
||||||
self.telegram = Updater(api.keys.telegram_api, use_context=True)
|
self.telegram = Updater(api.keys.telegram_api, use_context=True)
|
||||||
self.dispatcher = self.telegram.dispatcher
|
self.dispatcher = self.telegram.dispatcher
|
||||||
self.commands = commands
|
self.commands = commands
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_commands(self):
|
def add_commands(self):
|
||||||
# Mark modules as available
|
# Mark modules as available
|
||||||
db = self.db
|
db = self.db
|
||||||
self.help_module = self.commands.help.Help(db)
|
self.help_module = self.commands.help.Help(db)
|
||||||
self.sub_modules = {
|
self.sub_modules = {
|
||||||
"weather": self.commands.weather.Weather(self.api_weather, db),
|
"weather": self.commands.weather.Weather(self.api_weather, db),
|
||||||
"help" : self.help_module,
|
"help" : self.help_module,
|
||||||
"status" : self.commands.status.Status(self.name, self.version, db),
|
"status" : self.commands.status.Status(self.name, self.version, db),
|
||||||
"zvv" : self.commands.zvv.Zvv(db),
|
"zvv" : self.commands.zvv.Zvv(db),
|
||||||
"list" : self.commands.lists.Lists(db),
|
"list" : self.commands.lists.Lists(db),
|
||||||
# "alias" : commands.alias.Alias(self.dispatcher, db),
|
# "alias" : commands.alias.Alias(self.dispatcher, db),
|
||||||
"joke" : self.commands.reddit.Joke(self.api_reddit, db),
|
"joke" : self.commands.reddit.Joke(self.api_reddit, db),
|
||||||
"meme" : self.commands.reddit.Meme(self.api_reddit, db),
|
"meme" : self.commands.reddit.Meme(self.api_reddit, db),
|
||||||
# "news" : self.commands.reddit.News(self.api_reddit, db),
|
# "news" : self.commands.reddit.News(self.api_reddit, db),
|
||||||
"search" : self.commands.search.Search(self.api_search, db),
|
"search" : self.commands.search.Search(self.api_search, db),
|
||||||
# ...
|
# ...
|
||||||
"plaintext" : self.commands.plaintext.Plain(db) # for handling non-command messages that should simply contribute to statistics
|
"plaintext" : self.commands.plaintext.Plain(db) # for handling non-command messages that should simply contribute to statistics
|
||||||
}
|
}
|
||||||
# must be a class that has a method create_handler
|
# must be a class that has a method create_handler
|
||||||
|
|
||||||
for k in self.sub_modules:
|
for k in self.sub_modules:
|
||||||
self.dispatcher.add_handler(self.sub_modules[k].create_handler())
|
self.dispatcher.add_handler(self.sub_modules[k].create_handler())
|
||||||
|
|
||||||
self.help_module.add_commands(self.sub_modules)
|
self.help_module.add_commands(self.sub_modules)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.sub_modules = {"clock" : self.commands.clock.Clock(self.db, self.modules["clock"], self.api_art)}
|
self.sub_modules = {"clock" : self.commands.clock.Clock(self.db, self.modules["clock"], self.api_art)}
|
||||||
self.add_commands()
|
self.add_commands()
|
||||||
self.telegram.start_polling(
|
self.telegram.start_polling(
|
||||||
poll_interval=0.2,
|
poll_interval=0.2,
|
||||||
)
|
)
|
||||||
# self.telegram.idle()
|
# self.telegram.idle()
|
||||||
|
@ -1 +1 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
|
@ -1,71 +1,71 @@
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class FetchUpdates:
|
class FetchUpdates:
|
||||||
"""Fetches updates from the main server and relays them to the clock"""
|
"""Fetches updates from the main server and relays them to the clock"""
|
||||||
|
|
||||||
def __init__(self, server_ip, port):
|
def __init__(self, server_ip, port):
|
||||||
"""Both methods return a list as python-object. This should be then converted to a numpy array."""
|
"""Both methods return a list as python-object. This should be then converted to a numpy array."""
|
||||||
# self.server_ip = server_ip
|
# self.server_ip = server_ip
|
||||||
self.base_url = server_ip + ":" + port
|
self.base_url = server_ip + ":" + port
|
||||||
# self.modules gets added through the caller
|
# self.modules gets added through the caller
|
||||||
self.update_calls = 0
|
self.update_calls = 0
|
||||||
self.last_fetch = {}
|
self.last_fetch = {}
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
# dummy for errorless launching
|
# dummy for errorless launching
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_updates(self):
|
def get_updates(self):
|
||||||
update_url = "http://" + self.base_url + "/getupdates"
|
update_url = "http://" + self.base_url + "/getupdates"
|
||||||
result = self.call_api(update_url)
|
result = self.call_api(update_url)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_last(self):
|
def get_last(self):
|
||||||
update_url = "http://" + self.base_url + "/getlast"
|
update_url = "http://" + self.base_url + "/getlast"
|
||||||
result = self.call_api(update_url)
|
result = self.call_api(update_url)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def fetch_data(self):
|
def fetch_data(self):
|
||||||
try:
|
try:
|
||||||
if self.update_calls == 0:
|
if self.update_calls == 0:
|
||||||
fetch = self.get_last()
|
fetch = self.get_last()
|
||||||
else:
|
else:
|
||||||
fetch = self.get_updates()
|
fetch = self.get_updates()
|
||||||
if not fetch["is_new"]:
|
if not fetch["is_new"]:
|
||||||
fetch = self.last_fetch
|
fetch = self.last_fetch
|
||||||
else:
|
else:
|
||||||
self.last_fetch = fetch
|
self.last_fetch = fetch
|
||||||
|
|
||||||
data = fetch["data"]
|
data = fetch["data"]
|
||||||
has_queue = fetch["has_queue"]
|
has_queue = fetch["has_queue"]
|
||||||
except:
|
except:
|
||||||
data = {}
|
data = {}
|
||||||
has_queue = False
|
has_queue = False
|
||||||
|
|
||||||
self.update_calls += 1
|
self.update_calls += 1
|
||||||
|
|
||||||
return has_queue, data
|
return has_queue, data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def call_api(self, url):
|
def call_api(self, url):
|
||||||
ret = {}
|
ret = {}
|
||||||
try:
|
try:
|
||||||
result = requests.get(url)
|
result = requests.get(url)
|
||||||
result = result.json()
|
result = result.json()
|
||||||
|
|
||||||
if result.pop("status") == "ok":
|
if result.pop("status") == "ok":
|
||||||
ret = result
|
ret = result
|
||||||
except:
|
except:
|
||||||
logger.error("Bad api call for method {}.".format(url[url.rfind("/"):]))
|
logger.error("Bad api call for method {}.".format(url[url.rfind("/"):]))
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -1,86 +1,86 @@
|
|||||||
import flask
|
import flask
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('werkzeug')
|
log = logging.getLogger('werkzeug')
|
||||||
log.setLevel(logging.ERROR)
|
log.setLevel(logging.ERROR)
|
||||||
# hide the info-messages of each GET-request
|
# hide the info-messages of each GET-request
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BroadcastUpdates:
|
class BroadcastUpdates:
|
||||||
"""Broadcasts (out) updates for the hw-handler to be fetched periodically"""
|
"""Broadcasts (out) updates for the hw-handler to be fetched periodically"""
|
||||||
|
|
||||||
def __init__(self, port):
|
def __init__(self, port):
|
||||||
""""""
|
""""""
|
||||||
self.last_show = ""
|
self.last_show = ""
|
||||||
|
|
||||||
self.queue = [] #[{"matrices" : [np.full((16,16,3), 10).tolist(), np.full((16,16,3), 100).tolist(), np.full((16,16,3), 200).tolist()]} for _ in range(4)]
|
self.queue = [] #[{"matrices" : [np.full((16,16,3), 10).tolist(), np.full((16,16,3), 100).tolist(), np.full((16,16,3), 200).tolist()]} for _ in range(4)]
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
t = Thread(target=self.run)
|
t = Thread(target=self.run)
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
@app.route('/getupdates', methods=['GET'])
|
@app.route('/getupdates', methods=['GET'])
|
||||||
def get_updates():
|
def get_updates():
|
||||||
return self.get_updates()
|
return self.get_updates()
|
||||||
@app.route('/getlast', methods=['GET'])
|
@app.route('/getlast', methods=['GET'])
|
||||||
def get_last():
|
def get_last():
|
||||||
return self.get_last()
|
return self.get_last()
|
||||||
|
|
||||||
app.run('0.0.0.0', port=self.port)
|
app.run('0.0.0.0', port=self.port)
|
||||||
|
|
||||||
|
|
||||||
def get_updates(self):
|
def get_updates(self):
|
||||||
try:
|
try:
|
||||||
data = self.queue.pop(0)
|
data = self.queue.pop(0)
|
||||||
self.last_show = data
|
self.last_show = data
|
||||||
is_new = True
|
is_new = True
|
||||||
has_queue = len(self.queue) > 0
|
has_queue = len(self.queue) > 0
|
||||||
except:
|
except:
|
||||||
data = ""
|
data = ""
|
||||||
is_new = False
|
is_new = False
|
||||||
has_queue = False
|
has_queue = False
|
||||||
|
|
||||||
return self.generate_response(
|
return self.generate_response(
|
||||||
is_new = is_new,
|
is_new = is_new,
|
||||||
data = data,
|
data = data,
|
||||||
has_queue = has_queue
|
has_queue = has_queue
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_last(self):
|
def get_last(self):
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
data = self.queue[-1]
|
data = self.queue[-1]
|
||||||
self.queue = []
|
self.queue = []
|
||||||
except: # list empty
|
except: # list empty
|
||||||
data = self.last_show,
|
data = self.last_show,
|
||||||
except:
|
except:
|
||||||
data = ""
|
data = ""
|
||||||
return self.generate_response(
|
return self.generate_response(
|
||||||
data = data,
|
data = data,
|
||||||
has_queue = False,
|
has_queue = False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_response(self, **kwargs):
|
def generate_response(self, **kwargs):
|
||||||
ret = {
|
ret = {
|
||||||
"status" : "ok",
|
"status" : "ok",
|
||||||
**kwargs
|
**kwargs
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonify(ret)
|
return jsonify(ret)
|
||||||
|
|
||||||
|
84
client.py
84
client.py
@ -1,43 +1,43 @@
|
|||||||
# functionality
|
# functionality
|
||||||
from clock import c_in, c_out
|
from clock import c_in, c_out
|
||||||
from broadcast import b_in
|
from broadcast import b_in
|
||||||
|
|
||||||
import launcher
|
import launcher
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
# development
|
# development
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
filename='persistence/client.log',
|
filename='persistence/client.log',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ReceiverLauncher(launcher.Launcher):
|
class ReceiverLauncher(launcher.Launcher):
|
||||||
"""Launcher for all server-side modules. The hard-computations"""
|
"""Launcher for all server-side modules. The hard-computations"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.clock_sensor_module = c_in.SensorReadout()
|
self.clock_sensor_module = c_in.SensorReadout()
|
||||||
# active: periodically takes readouts
|
# active: periodically takes readouts
|
||||||
self.clock_hardware_module = c_out.ClockFace()
|
self.clock_hardware_module = c_out.ClockFace()
|
||||||
# active: periodically calls fetcher
|
# active: periodically calls fetcher
|
||||||
self.receive_module = b_in.FetchUpdates(server_ip="localhost", port="1111")
|
self.receive_module = b_in.FetchUpdates(server_ip="localhost", port="1111")
|
||||||
# passive: fetches data on demand
|
# passive: fetches data on demand
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
sensors = self.clock_sensor_module,
|
sensors = self.clock_sensor_module,
|
||||||
clock = self.clock_hardware_module,
|
clock = self.clock_hardware_module,
|
||||||
receive = self.receive_module
|
receive = self.receive_module
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ReceiverLauncher()
|
ReceiverLauncher()
|
@ -1 +1 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
|
146
clock/c_back.py
146
clock/c_back.py
@ -1,73 +1,73 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
class ClockBackend:
|
class ClockBackend:
|
||||||
"""Heavy lifting of matrix operations"""
|
"""Heavy lifting of matrix operations"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.MOP = helpers.computations.MatrixOperations()
|
self.MOP = helpers.computations.MatrixOperations()
|
||||||
|
|
||||||
self.weather = {"weather":"", "high":"", "low":""}
|
self.weather = {"weather":"", "high":"", "low":""}
|
||||||
self.weather_raw = {}
|
self.weather_raw = {}
|
||||||
self.weather_face_swap = False
|
self.weather_face_swap = False
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.out = self.modules["broadcast"]
|
self.out = self.modules["broadcast"]
|
||||||
helpers.timer.RepeatedTimer(15, self.clock_loop)
|
helpers.timer.RepeatedTimer(15, self.clock_loop)
|
||||||
|
|
||||||
|
|
||||||
def clock_loop(self):
|
def clock_loop(self):
|
||||||
t = int(datetime.datetime.now().strftime("%H%M"))
|
t = int(datetime.datetime.now().strftime("%H%M"))
|
||||||
|
|
||||||
if t % 5 == 0:
|
if t % 5 == 0:
|
||||||
# switch secondary face every 5 minutes
|
# switch secondary face every 5 minutes
|
||||||
weather = self.modules["bot"].api_weather.show_weather([47.3769, 8.5417]) # zürich
|
weather = self.modules["bot"].api_weather.show_weather([47.3769, 8.5417]) # zürich
|
||||||
|
|
||||||
if weather != self.weather_raw and len(weather) != 0:
|
if weather != self.weather_raw and len(weather) != 0:
|
||||||
td = weather[1]
|
td = weather[1]
|
||||||
low = td["temps"][0]
|
low = td["temps"][0]
|
||||||
high = td["temps"][1]
|
high = td["temps"][1]
|
||||||
self.weather["weather"] = td["short"]
|
self.weather["weather"] = td["short"]
|
||||||
self.weather["high"] = high
|
self.weather["high"] = high
|
||||||
self.weather["low"] = low
|
self.weather["low"] = low
|
||||||
elif len(weather) == 0:
|
elif len(weather) == 0:
|
||||||
self.weather["weather"] = "error"
|
self.weather["weather"] = "error"
|
||||||
self.weather["high"] = "error"
|
self.weather["high"] = "error"
|
||||||
self.weather["low"] = "error"
|
self.weather["low"] = "error"
|
||||||
|
|
||||||
self.weather_face_swap = not self.weather_face_swap
|
self.weather_face_swap = not self.weather_face_swap
|
||||||
|
|
||||||
self.send_face()
|
self.send_face()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def send_face(self):
|
def send_face(self):
|
||||||
"""Set the clock face (time + weather) by getting updated info - gets called every minute"""
|
"""Set the clock face (time + weather) by getting updated info - gets called every minute"""
|
||||||
matrices = self.MOP.clock_face(self.weather)
|
matrices = self.MOP.clock_face(self.weather)
|
||||||
if self.weather_face_swap:
|
if self.weather_face_swap:
|
||||||
matrices = [matrices[0], matrices[2], matrices[1]]
|
matrices = [matrices[0], matrices[2], matrices[1]]
|
||||||
|
|
||||||
matrices = [m.tolist() for m in matrices]
|
matrices = [m.tolist() for m in matrices]
|
||||||
self.out.queue.append({"matrices" : matrices})
|
self.out.queue.append({"matrices" : matrices})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def text_scroll(self, text, color=[[200,200,200]]):
|
# def text_scroll(self, text, color=[[200,200,200]]):
|
||||||
# pixels = self.MOP.text_converter(text, 12, color)
|
# pixels = self.MOP.text_converter(text, 12, color)
|
||||||
# sleep_time = 1 / self.tspeed
|
# sleep_time = 1 / self.tspeed
|
||||||
# width = self.shape[1]
|
# width = self.shape[1]
|
||||||
# frames = pixels.shape[1] - width
|
# frames = pixels.shape[1] - width
|
||||||
# if frames <= 0:
|
# if frames <= 0:
|
||||||
# frames = 1
|
# frames = 1
|
||||||
|
|
||||||
# for i in range(frames):
|
# for i in range(frames):
|
||||||
# visible = pixels[:,i:width+i]
|
# visible = pixels[:,i:width+i]
|
||||||
# self.IO.put(visible*self.brightness)
|
# self.IO.put(visible*self.brightness)
|
||||||
# time.sleep(sleep_time)
|
# time.sleep(sleep_time)
|
||||||
# time.sleep(10 * sleep_time)
|
# time.sleep(10 * sleep_time)
|
||||||
|
|
||||||
|
|
||||||
|
112
clock/c_in.py
112
clock/c_in.py
@ -1,56 +1,56 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
from threading import Thread, Timer
|
from threading import Thread, Timer
|
||||||
|
|
||||||
from . import hardware, helpers
|
from . import hardware, helpers
|
||||||
|
|
||||||
|
|
||||||
class SensorReadout:
|
class SensorReadout:
|
||||||
"""Overview class for (actual and potential) sensor sources"""
|
"""Overview class for (actual and potential) sensor sources"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
""""""
|
""""""
|
||||||
self.sensor_modules = { # we already call them, they are objects and not classes anymore
|
self.sensor_modules = { # we already call them, they are objects and not classes anymore
|
||||||
"temperature" : hardware.sensors.TemperatureModule(),
|
"temperature" : hardware.sensors.TemperatureModule(),
|
||||||
"humidity" : hardware.sensors.HumidityModule(),
|
"humidity" : hardware.sensors.HumidityModule(),
|
||||||
"luminosity" : hardware.sensors.BrightnessModule(),
|
"luminosity" : hardware.sensors.BrightnessModule(),
|
||||||
# more to come?
|
# more to come?
|
||||||
}
|
}
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
helpers.timer.RepeatedTimer(300, self.spread_measure)
|
helpers.timer.RepeatedTimer(300, self.spread_measure)
|
||||||
|
|
||||||
def spread_measure(self):
|
def spread_measure(self):
|
||||||
measurements = dict((el,[]) for el in self.sensor_modules.keys())
|
measurements = dict((el,[]) for el in self.sensor_modules.keys())
|
||||||
# create an empty dict with a list for each readout-type
|
# create an empty dict with a list for each readout-type
|
||||||
|
|
||||||
for _ in range(5): # number of measures to average out
|
for _ in range(5): # number of measures to average out
|
||||||
for name in self.sensor_modules.keys():
|
for name in self.sensor_modules.keys():
|
||||||
measure = self.sensor_modules[name].readout()
|
measure = self.sensor_modules[name].readout()
|
||||||
measurements[name].append(measure)
|
measurements[name].append(measure)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
for e in measurements.keys():
|
for e in measurements.keys():
|
||||||
lst = measurements[e]
|
lst = measurements[e]
|
||||||
results[e] = int(sum(lst) / len(lst))
|
results[e] = int(sum(lst) / len(lst))
|
||||||
|
|
||||||
self.save_results(**results)
|
self.save_results(**results)
|
||||||
|
|
||||||
|
|
||||||
# def save_results(self, results):
|
# def save_results(self, results):
|
||||||
# current_minute = int(datetime.datetime.now().timestamp() // 60)
|
# current_minute = int(datetime.datetime.now().timestamp() // 60)
|
||||||
|
|
||||||
# self.persistence["clock"]["sensors"]["time"] += [current_minute]
|
# self.persistence["clock"]["sensors"]["time"] += [current_minute]
|
||||||
|
|
||||||
# for name in results.keys():
|
# for name in results.keys():
|
||||||
# keep_value = sum(results[name]) / len(results[name])
|
# keep_value = sum(results[name]) / len(results[name])
|
||||||
# self.persistence["clock"]["sensors"][name] += [keep_value]
|
# self.persistence["clock"]["sensors"][name] += [keep_value]
|
||||||
|
|
||||||
|
|
||||||
def save_results(self, **results):
|
def save_results(self, **results):
|
||||||
data = self.db.sensors(
|
data = self.db.sensors(
|
||||||
time=datetime.datetime.now(),
|
time=datetime.datetime.now(),
|
||||||
**results,
|
**results,
|
||||||
)
|
)
|
||||||
data.save()
|
data.save()
|
||||||
|
130
clock/c_out.py
130
clock/c_out.py
@ -1,65 +1,65 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from . import hardware, helpers
|
from . import hardware, helpers
|
||||||
|
|
||||||
|
|
||||||
class ClockFace:
|
class ClockFace:
|
||||||
"""Actual functions one might need for a clock"""
|
"""Actual functions one might need for a clock"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
""""""
|
""""""
|
||||||
# added by the launcher, we have self.modules (dict)
|
# added by the launcher, we have self.modules (dict)
|
||||||
|
|
||||||
self.IO = hardware.led.get_handler()
|
self.IO = hardware.led.get_handler()
|
||||||
self.shape = self.IO.shape # (16,32) for now
|
self.shape = self.IO.shape # (16,32) for now
|
||||||
# TODO handle differently!
|
# TODO handle differently!
|
||||||
self.MOP = helpers.computations.MatrixOperations()
|
self.MOP = helpers.computations.MatrixOperations()
|
||||||
|
|
||||||
self.kill_output = False
|
self.kill_output = False
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
Thread(target = self.clock_loop).start()
|
Thread(target = self.clock_loop).start()
|
||||||
|
|
||||||
|
|
||||||
def clock_loop(self):
|
def clock_loop(self):
|
||||||
while True: # TODO: allow this to be exited gracefully
|
while True: # TODO: allow this to be exited gracefully
|
||||||
|
|
||||||
t_start = datetime.datetime.now()
|
t_start = datetime.datetime.now()
|
||||||
|
|
||||||
self.set_brightness()
|
self.set_brightness()
|
||||||
|
|
||||||
has_queue, data = self.modules["receive"].fetch_data()
|
has_queue, data = self.modules["receive"].fetch_data()
|
||||||
tnext = 1 if has_queue else 30
|
tnext = 1 if has_queue else 30
|
||||||
|
|
||||||
if data == {}:
|
if data == {}:
|
||||||
matrices = self.MOP.get_fallback()
|
matrices = self.MOP.get_fallback()
|
||||||
matrices[0][0,0] = [255, 0, 0] # red dot on the top left
|
matrices[0][0,0] = [255, 0, 0] # red dot on the top left
|
||||||
else:
|
else:
|
||||||
matrices = [np.asarray(d).astype(int) for d in data["matrices"]]
|
matrices = [np.asarray(d).astype(int) for d in data["matrices"]]
|
||||||
matrices[0][0,0] = [0, 255, 0] # green dot on the top left
|
matrices[0][0,0] = [0, 255, 0] # green dot on the top left
|
||||||
|
|
||||||
if not self.kill_output:
|
if not self.kill_output:
|
||||||
self.IO.put(matrices)
|
self.IO.put(matrices)
|
||||||
else:
|
else:
|
||||||
z = np.zeros((16,16,3))
|
z = np.zeros((16,16,3))
|
||||||
self.IO.put([z,z,z])
|
self.IO.put([z,z,z])
|
||||||
|
|
||||||
|
|
||||||
t_end = datetime.datetime.now()
|
t_end = datetime.datetime.now()
|
||||||
delta_planned = datetime.timedelta(seconds = tnext)
|
delta_planned = datetime.timedelta(seconds = tnext)
|
||||||
delta = delta_planned - (t_end - t_start)
|
delta = delta_planned - (t_end - t_start)
|
||||||
|
|
||||||
time.sleep(max(delta.total_seconds(), 0))
|
time.sleep(max(delta.total_seconds(), 0))
|
||||||
|
|
||||||
|
|
||||||
def set_brightness(self):
|
def set_brightness(self):
|
||||||
"""Kill the brightness at night"""
|
"""Kill the brightness at night"""
|
||||||
|
|
||||||
is_WE = datetime.datetime.now().weekday() > 4
|
is_WE = datetime.datetime.now().weekday() > 4
|
||||||
now = int(datetime.datetime.now().strftime("%H%M"))
|
now = int(datetime.datetime.now().strftime("%H%M"))
|
||||||
|
|
||||||
self.kill_output = (now < 1000 or now > 2200) if is_WE else (now < 830 or now > 2130)
|
self.kill_output = (now < 1000 or now > 2200) if is_WE else (now < 830 or now > 2130)
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
from . import led, sensors
|
from . import led, sensors
|
@ -1,17 +1,17 @@
|
|||||||
from . import unicorn as led
|
from . import unicorn as led
|
||||||
# or neopixel soon:
|
# or neopixel soon:
|
||||||
# from . import neopixel as led
|
# from . import neopixel as led
|
||||||
|
|
||||||
def get_handler():
|
def get_handler():
|
||||||
OUT = led.ClockOut()
|
OUT = led.ClockOut()
|
||||||
shape = OUT.shape
|
shape = OUT.shape
|
||||||
|
|
||||||
if led.SETUP_FAIL:
|
if led.SETUP_FAIL:
|
||||||
# we use the sim
|
# we use the sim
|
||||||
del OUT
|
del OUT
|
||||||
from . import sim
|
from . import sim
|
||||||
OUT = sim.ClockOut(shape)
|
OUT = sim.ClockOut(shape)
|
||||||
|
|
||||||
return OUT
|
return OUT
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,51 +1,51 @@
|
|||||||
import time
|
import time
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import colorsys
|
import colorsys
|
||||||
import random
|
import random
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import rpi_ws281x as ws
|
import rpi_ws281x as ws
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
ws = Mock()
|
ws = Mock()
|
||||||
SETUP_FAIL = True
|
SETUP_FAIL = True
|
||||||
|
|
||||||
|
|
||||||
class ClockOut:
|
class ClockOut:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.shape = (45, 20) # H x W
|
self.shape = (45, 20) # H x W
|
||||||
num = self.shape[0] * self.shape[1]
|
num = self.shape[0] * self.shape[1]
|
||||||
pin = 18
|
pin = 18
|
||||||
freq = 800000 # maybe higher
|
freq = 800000 # maybe higher
|
||||||
dma = 5
|
dma = 5
|
||||||
invert = False
|
invert = False
|
||||||
brightness = 100
|
brightness = 100
|
||||||
channel = 0
|
channel = 0
|
||||||
led_type = None # ??
|
led_type = None # ??
|
||||||
self.strip = ws.PixelStrip(num, pin, freq, dma, invert, brightness, channel, led_type)
|
self.strip = ws.PixelStrip(num, pin, freq, dma, invert, brightness, channel, led_type)
|
||||||
self.strip.begin()
|
self.strip.begin()
|
||||||
|
|
||||||
|
|
||||||
def put(self, matrix):
|
def put(self, matrix):
|
||||||
self.render(matrix)
|
self.render(matrix)
|
||||||
|
|
||||||
|
|
||||||
def render(self, matrix):
|
def render(self, matrix):
|
||||||
p = 0
|
p = 0
|
||||||
for i in range(matrix.shape[0]):
|
for i in range(matrix.shape[0]):
|
||||||
for j in range(matrix.shape[1]):
|
for j in range(matrix.shape[1]):
|
||||||
col = int(ws.Color(*matrix[i,j]))
|
col = int(ws.Color(*matrix[i,j]))
|
||||||
|
|
||||||
self.strip.setPixelColor(p, col)
|
self.strip.setPixelColor(p, col)
|
||||||
p += 1
|
p += 1
|
||||||
self.strip.show()
|
self.strip.show()
|
||||||
|
|
||||||
|
|
||||||
# test = ClockOut()
|
# test = ClockOut()
|
||||||
# z = np.zeros((30,30, 3), dtype=int)
|
# z = np.zeros((30,30, 3), dtype=int)
|
||||||
# for i in range(30):
|
# for i in range(30):
|
||||||
# for j in range(30):
|
# for j in range(30):
|
||||||
# z[i, j, ...] = [random.randint(0,255), random.randint(0,255), random.randint(0,255)]
|
# z[i, j, ...] = [random.randint(0,255), random.randint(0,255), random.randint(0,255)]
|
||||||
# test.put(z)
|
# test.put(z)
|
||||||
# #time.sleep(0.1)
|
# #time.sleep(0.1)
|
||||||
|
|
||||||
|
@ -1,87 +1,87 @@
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class TempSim:
|
class TempSim:
|
||||||
"""Simulates a temperature for running on windows"""
|
"""Simulates a temperature for running on windows"""
|
||||||
temperature = 23 # return a celsius value
|
temperature = 23 # return a celsius value
|
||||||
humidity = 30
|
humidity = 30
|
||||||
|
|
||||||
|
|
||||||
class LightSim:
|
class LightSim:
|
||||||
def input(self, *args):
|
def input(self, *args):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SensorModule:
|
class SensorModule:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logger.info("Using module " + self.__class__.__name__)
|
logger.info("Using module " + self.__class__.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Real sensors!
|
## Real sensors!
|
||||||
try:
|
try:
|
||||||
import board
|
import board
|
||||||
import adafruit_dht
|
import adafruit_dht
|
||||||
dht11 = adafruit_dht.DHT11(board.D18)
|
dht11 = adafruit_dht.DHT11(board.D18)
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
GPIO.setup(4, GPIO.IN)
|
GPIO.setup(4, GPIO.IN)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warn("Simulating sensor modules")
|
logger.warn("Simulating sensor modules")
|
||||||
dht11 = TempSim()
|
dht11 = TempSim()
|
||||||
GPIO = LightSim()
|
GPIO = LightSim()
|
||||||
|
|
||||||
|
|
||||||
class TemperatureModule(SensorModule):
|
class TemperatureModule(SensorModule):
|
||||||
"""Takes readouts from the DHT 11
|
"""Takes readouts from the DHT 11
|
||||||
Returns: temperature"""
|
Returns: temperature"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.device = dht11
|
self.device = dht11
|
||||||
|
|
||||||
def readout(self):
|
def readout(self):
|
||||||
try:
|
try:
|
||||||
temp = self.device.temperature
|
temp = self.device.temperature
|
||||||
except:
|
except:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
try:
|
try:
|
||||||
temp = self.device.temperature
|
temp = self.device.temperature
|
||||||
except:
|
except:
|
||||||
temp = -1
|
temp = -1
|
||||||
|
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
class HumidityModule(SensorModule):
|
class HumidityModule(SensorModule):
|
||||||
"""Takes readouts from the DHT 11
|
"""Takes readouts from the DHT 11
|
||||||
Returns: humidity"""
|
Returns: humidity"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.device = dht11
|
self.device = dht11
|
||||||
|
|
||||||
def readout(self):
|
def readout(self):
|
||||||
try:
|
try:
|
||||||
hum = self.device.humidity
|
hum = self.device.humidity
|
||||||
except:
|
except:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
try:
|
try:
|
||||||
hum = self.device.humidity
|
hum = self.device.humidity
|
||||||
except:
|
except:
|
||||||
hum = -1
|
hum = -1
|
||||||
|
|
||||||
return hum
|
return hum
|
||||||
|
|
||||||
class BrightnessModule(SensorModule):
|
class BrightnessModule(SensorModule):
|
||||||
"""Returns one for HIGH and zero for LOW"""
|
"""Returns one for HIGH and zero for LOW"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def readout(self):
|
def readout(self):
|
||||||
# The sensor is reversed: 0 when bright and 1 if dark
|
# The sensor is reversed: 0 when bright and 1 if dark
|
||||||
light = GPIO.input(4)
|
light = GPIO.input(4)
|
||||||
if light == 0:
|
if light == 0:
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
import sys
|
import sys
|
||||||
import colorsys
|
import colorsys
|
||||||
import pygame.gfxdraw
|
import pygame.gfxdraw
|
||||||
import time
|
import time
|
||||||
import pygame
|
import pygame
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
class ClockOut:
|
class ClockOut:
|
||||||
"""Creates a drawable window in case the real hardware is not accessible. For development"""
|
"""Creates a drawable window in case the real hardware is not accessible. For development"""
|
||||||
def __init__(self, shape):
|
def __init__(self, shape):
|
||||||
self.pixel_size = 20
|
self.pixel_size = 20
|
||||||
|
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.pixels = np.zeros((*shape,3), dtype=int)
|
self.pixels = np.zeros((*shape,3), dtype=int)
|
||||||
self.WIDTH = shape[1]
|
self.WIDTH = shape[1]
|
||||||
self.HEIGHT = shape[0]
|
self.HEIGHT = shape[0]
|
||||||
self.window_width = self.WIDTH * self.pixel_size
|
self.window_width = self.WIDTH * self.pixel_size
|
||||||
self.window_height = self.HEIGHT * self.pixel_size
|
self.window_height = self.HEIGHT * self.pixel_size
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
pygame.display.set_caption("Unicorn HAT simulator")
|
pygame.display.set_caption("Unicorn HAT simulator")
|
||||||
self.screen = pygame.display.set_mode([self.window_width, self.window_height])
|
self.screen = pygame.display.set_mode([self.window_width, self.window_height])
|
||||||
|
|
||||||
|
|
||||||
def put(self, matrices):
|
def put(self, matrices):
|
||||||
self.screen.fill((0, 0, 0))
|
self.screen.fill((0, 0, 0))
|
||||||
for event in pygame.event.get(): # User did something
|
for event in pygame.event.get(): # User did something
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if self.shape == (16, 32):
|
if self.shape == (16, 32):
|
||||||
matrix = np.concatenate((matrices[0], matrices[1]), axis=1)
|
matrix = np.concatenate((matrices[0], matrices[1]), axis=1)
|
||||||
|
|
||||||
self.pixels = matrix
|
self.pixels = matrix
|
||||||
self.draw_pixels()
|
self.draw_pixels()
|
||||||
|
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
pygame.event.pump()
|
pygame.event.pump()
|
||||||
|
|
||||||
|
|
||||||
def draw_pixels(self):
|
def draw_pixels(self):
|
||||||
p = self.pixel_size
|
p = self.pixel_size
|
||||||
|
|
||||||
r = int(p / 4)
|
r = int(p / 4)
|
||||||
for i in range(self.HEIGHT):
|
for i in range(self.HEIGHT):
|
||||||
for j in range(self.WIDTH):
|
for j in range(self.WIDTH):
|
||||||
w_x = int(j * p + p / 2)
|
w_x = int(j * p + p / 2)
|
||||||
#w_y = int((self.HEIGHT - 1 - y) * p + p / 2)
|
#w_y = int((self.HEIGHT - 1 - y) * p + p / 2)
|
||||||
w_y = int(i * p + p / 2)
|
w_y = int(i * p + p / 2)
|
||||||
color = self.pixels[i,j,:]
|
color = self.pixels[i,j,:]
|
||||||
color = color.astype("int")
|
color = color.astype("int")
|
||||||
|
|
||||||
pygame.gfxdraw.aacircle(self.screen, w_x, w_y, r, color)
|
pygame.gfxdraw.aacircle(self.screen, w_x, w_y, r, color)
|
||||||
pygame.gfxdraw.filled_circle(self.screen, w_x, w_y, r, color)
|
pygame.gfxdraw.filled_circle(self.screen, w_x, w_y, r, color)
|
||||||
|
@ -1,96 +1,96 @@
|
|||||||
import colorsys
|
import colorsys
|
||||||
import time
|
import time
|
||||||
import numpy as np
|
import numpy as np
|
||||||
try:
|
try:
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
SETUP_FAIL = False
|
SETUP_FAIL = False
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
GPIO = Mock()
|
GPIO = Mock()
|
||||||
SETUP_FAIL = True
|
SETUP_FAIL = True
|
||||||
|
|
||||||
|
|
||||||
class ClockOut:
|
class ClockOut:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.PIN_CLK = 11
|
self.PIN_CLK = 11
|
||||||
##################################
|
##################################
|
||||||
# Hardcoded vaules:
|
# Hardcoded vaules:
|
||||||
# GPIO Pins for the actual signal. The other ones are for signal clocks and resets.
|
# GPIO Pins for the actual signal. The other ones are for signal clocks and resets.
|
||||||
self.PINS_DAT = [10, 22]
|
self.PINS_DAT = [10, 22]
|
||||||
self.PIN_CS = 8
|
self.PIN_CS = 8
|
||||||
# for data transmission
|
# for data transmission
|
||||||
self.SOF = 0x72
|
self.SOF = 0x72
|
||||||
self.DELAY = 1.0/120
|
self.DELAY = 1.0/120
|
||||||
# shape of 2 unicorn hats
|
# shape of 2 unicorn hats
|
||||||
self.shape = (16, 32)
|
self.shape = (16, 32)
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
GPIO.setwarnings(False)
|
GPIO.setwarnings(False)
|
||||||
GPIO.setup(self.PIN_CS, GPIO.OUT, initial=GPIO.HIGH)
|
GPIO.setup(self.PIN_CS, GPIO.OUT, initial=GPIO.HIGH)
|
||||||
GPIO.setup(self.PIN_CLK, GPIO.OUT, initial=GPIO.LOW)
|
GPIO.setup(self.PIN_CLK, GPIO.OUT, initial=GPIO.LOW)
|
||||||
GPIO.setup(self.PINS_DAT, GPIO.OUT, initial=GPIO.LOW)
|
GPIO.setup(self.PINS_DAT, GPIO.OUT, initial=GPIO.LOW)
|
||||||
|
|
||||||
self.HEIGHT = self.shape[0] #16
|
self.HEIGHT = self.shape[0] #16
|
||||||
self.WIDTH = self.shape[1] #32
|
self.WIDTH = self.shape[1] #32
|
||||||
|
|
||||||
self.reset_clock()
|
self.reset_clock()
|
||||||
|
|
||||||
|
|
||||||
def reset_clock(self):
|
def reset_clock(self):
|
||||||
GPIO.output(self.PIN_CS, GPIO.LOW)
|
GPIO.output(self.PIN_CS, GPIO.LOW)
|
||||||
time.sleep(0.00001)
|
time.sleep(0.00001)
|
||||||
GPIO.output(self.PIN_CS, GPIO.HIGH)
|
GPIO.output(self.PIN_CS, GPIO.HIGH)
|
||||||
|
|
||||||
|
|
||||||
def spi_write(self, buf1, buf2):
|
def spi_write(self, buf1, buf2):
|
||||||
GPIO.output(self.PIN_CS, GPIO.LOW)
|
GPIO.output(self.PIN_CS, GPIO.LOW)
|
||||||
|
|
||||||
self.spi_write_byte(self.SOF, self.SOF)
|
self.spi_write_byte(self.SOF, self.SOF)
|
||||||
|
|
||||||
for x in range(len(buf1)):
|
for x in range(len(buf1)):
|
||||||
b1, b2= buf1[x], buf2[x]
|
b1, b2= buf1[x], buf2[x]
|
||||||
self.spi_write_byte(b1, b2)
|
self.spi_write_byte(b1, b2)
|
||||||
|
|
||||||
|
|
||||||
GPIO.output(self.PIN_CS, GPIO.HIGH)
|
GPIO.output(self.PIN_CS, GPIO.HIGH)
|
||||||
|
|
||||||
|
|
||||||
def spi_write_byte(self, b1, b2):
|
def spi_write_byte(self, b1, b2):
|
||||||
for x in range(8):
|
for x in range(8):
|
||||||
GPIO.output(self.PINS_DAT[0], b1 & 0b10000000)
|
GPIO.output(self.PINS_DAT[0], b1 & 0b10000000)
|
||||||
GPIO.output(self.PINS_DAT[1], b2 & 0b10000000)
|
GPIO.output(self.PINS_DAT[1], b2 & 0b10000000)
|
||||||
GPIO.output(self.PIN_CLK, GPIO.HIGH)
|
GPIO.output(self.PIN_CLK, GPIO.HIGH)
|
||||||
|
|
||||||
b1 <<= 1
|
b1 <<= 1
|
||||||
b2 <<= 1
|
b2 <<= 1
|
||||||
#time.sleep(0.00000001)
|
#time.sleep(0.00000001)
|
||||||
GPIO.output(self.PIN_CLK, GPIO.LOW)
|
GPIO.output(self.PIN_CLK, GPIO.LOW)
|
||||||
|
|
||||||
|
|
||||||
def put(self, matrices):
|
def put(self, matrices):
|
||||||
"""Sets a height x width matrix directly"""
|
"""Sets a height x width matrix directly"""
|
||||||
self.reset_clock()
|
self.reset_clock()
|
||||||
matrix = np.concatenate((matrices[0], matrices[1]), axis=1) # or 1??
|
matrix = np.concatenate((matrices[0], matrices[1]), axis=1) # or 1??
|
||||||
self.show(matrix)
|
self.show(matrix)
|
||||||
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Clear the buffer."""
|
"""Clear the buffer."""
|
||||||
zero = np.zero((self.HEIGHT, self. WIDTH))
|
zero = np.zero((self.HEIGHT, self. WIDTH))
|
||||||
self.put(zero)
|
self.put(zero)
|
||||||
|
|
||||||
|
|
||||||
def show(self, matrix):
|
def show(self, matrix):
|
||||||
"""Output the contents of the buffer to Unicorn HAT HD."""
|
"""Output the contents of the buffer to Unicorn HAT HD."""
|
||||||
##########################################################
|
##########################################################
|
||||||
## Change to desire
|
## Change to desire
|
||||||
buff2 = np.rot90(matrix[:self.HEIGHT,:16],3)
|
buff2 = np.rot90(matrix[:self.HEIGHT,:16],3)
|
||||||
buff1 = np.rot90(matrix[:self.HEIGHT,16:32],1)
|
buff1 = np.rot90(matrix[:self.HEIGHT,16:32],1)
|
||||||
##########################################################
|
##########################################################
|
||||||
# separated these are: 16x16x3 arrays
|
# separated these are: 16x16x3 arrays
|
||||||
buff1, buff2 = [(x.reshape(768)).astype(np.uint8).tolist() for x in (buff1, buff2)]
|
buff1, buff2 = [(x.reshape(768)).astype(np.uint8).tolist() for x in (buff1, buff2)]
|
||||||
|
|
||||||
self.spi_write(buff1, buff2)
|
self.spi_write(buff1, buff2)
|
||||||
|
|
||||||
time.sleep(self.DELAY)
|
time.sleep(self.DELAY)
|
||||||
|
@ -1,178 +1,178 @@
|
|||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# bulky hard-coded values:
|
# bulky hard-coded values:
|
||||||
from . import shapes
|
from . import shapes
|
||||||
digits = shapes.digits
|
digits = shapes.digits
|
||||||
weather_categories = shapes.weather_categories
|
weather_categories = shapes.weather_categories
|
||||||
digit_position = [[2,4], [2,10], [9,4], [9,10]]
|
digit_position = [[2,4], [2,10], [9,4], [9,10]]
|
||||||
|
|
||||||
|
|
||||||
days = np.append(np.zeros((15,16)), np.array([0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1])).reshape((16,16))
|
days = np.append(np.zeros((15,16)), np.array([0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1])).reshape((16,16))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MatrixOperations():
|
class MatrixOperations():
|
||||||
"""Helper functions to generate frequently-used images"""
|
"""Helper functions to generate frequently-used images"""
|
||||||
def __init__(self, shape=[16,16]):
|
def __init__(self, shape=[16,16]):
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
# shape is going to be (16,32) for the moment
|
# shape is going to be (16,32) for the moment
|
||||||
self.primary = [200, 200, 200]
|
self.primary = [200, 200, 200]
|
||||||
self.secondary = [10, 200, 10]
|
self.secondary = [10, 200, 10]
|
||||||
self.error = [200, 10, 10]
|
self.error = [200, 10, 10]
|
||||||
|
|
||||||
|
|
||||||
def time_converter(self, top="", bottom=""):
|
def time_converter(self, top="", bottom=""):
|
||||||
"""Converts 4-digit time to a 16x16 pixel-matrix
|
"""Converts 4-digit time to a 16x16 pixel-matrix
|
||||||
returns: np.array"""
|
returns: np.array"""
|
||||||
# nshape = (self.shape[0], int(self.shape[1]/2))
|
# nshape = (self.shape[0], int(self.shape[1]/2))
|
||||||
nshape = (16, 16)
|
nshape = (16, 16)
|
||||||
pixels = np.zeros(nshape, dtype=np.uint8)
|
pixels = np.zeros(nshape, dtype=np.uint8)
|
||||||
|
|
||||||
if bottom == "" or top == "":
|
if bottom == "" or top == "":
|
||||||
top = datetime.datetime.now().strftime("%H")
|
top = datetime.datetime.now().strftime("%H")
|
||||||
bottom = datetime.datetime.now().strftime("%M")
|
bottom = datetime.datetime.now().strftime("%M")
|
||||||
|
|
||||||
if len(top) < 2:
|
if len(top) < 2:
|
||||||
top = "0" * (2 - len(top)) + top
|
top = "0" * (2 - len(top)) + top
|
||||||
if len(bottom) < 2:
|
if len(bottom) < 2:
|
||||||
bottom = "0" * (2 - len(bottom)) + bottom
|
bottom = "0" * (2 - len(bottom)) + bottom
|
||||||
|
|
||||||
if ("-" in top and len(top) > 2) or ("-" in bottom and len(bottom) > 2):
|
if ("-" in top and len(top) > 2) or ("-" in bottom and len(bottom) > 2):
|
||||||
time_split = 4*["-"]
|
time_split = 4*["-"]
|
||||||
elif "error" in top and "error" in bottom:
|
elif "error" in top and "error" in bottom:
|
||||||
time_split = 4*["error"]
|
time_split = 4*["error"]
|
||||||
else:
|
else:
|
||||||
time_split = [i for i in top] + [i for i in bottom]
|
time_split = [i for i in top] + [i for i in bottom]
|
||||||
|
|
||||||
if "-1" in top and len(top) != 2:
|
if "-1" in top and len(top) != 2:
|
||||||
time_split = ["-1", top[-1]] + [i for i in bottom]
|
time_split = ["-1", top[-1]] + [i for i in bottom]
|
||||||
if "-1" in bottom and len(bottom) != 2:
|
if "-1" in bottom and len(bottom) != 2:
|
||||||
time_split = [i for i in top] + ["-1", bottom[-1]]
|
time_split = [i for i in top] + ["-1", bottom[-1]]
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
x = digit_position[i][0]
|
x = digit_position[i][0]
|
||||||
y = digit_position[i][1]
|
y = digit_position[i][1]
|
||||||
number = digits[time_split[i]]
|
number = digits[time_split[i]]
|
||||||
pixels[x: x + 5, y: y + 3] = np.array(number)
|
pixels[x: x + 5, y: y + 3] = np.array(number)
|
||||||
|
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
def date_converter(self):
|
def date_converter(self):
|
||||||
# nshape = (self.shape[0], int(self.shape[1]/2))
|
# nshape = (self.shape[0], int(self.shape[1]/2))
|
||||||
nshape = (16, 16)
|
nshape = (16, 16)
|
||||||
today = datetime.datetime.today()
|
today = datetime.datetime.today()
|
||||||
weekday = datetime.datetime.weekday(today)
|
weekday = datetime.datetime.weekday(today)
|
||||||
# size of the reduced array according to weekday
|
# size of the reduced array according to weekday
|
||||||
size = [2,4,6,8,10,13,16]
|
size = [2,4,6,8,10,13,16]
|
||||||
|
|
||||||
pixels = days.copy() #base color background
|
pixels = days.copy() #base color background
|
||||||
lrow = np.append(pixels[15,:size[weekday]], [0 for i in range(16 - size[weekday])])
|
lrow = np.append(pixels[15,:size[weekday]], [0 for i in range(16 - size[weekday])])
|
||||||
lrow = np.append(np.zeros((15,16)), lrow).reshape(nshape)
|
lrow = np.append(np.zeros((15,16)), lrow).reshape(nshape)
|
||||||
pixels += lrow
|
pixels += lrow
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
def weather_converter(self, name):
|
def weather_converter(self, name):
|
||||||
"""Fills one half of the screen with weather info."""
|
"""Fills one half of the screen with weather info."""
|
||||||
# nshape = (self.shape[0], int(self.shape[1]/2))
|
# nshape = (self.shape[0], int(self.shape[1]/2))
|
||||||
nshape = (16, 16)
|
nshape = (16, 16)
|
||||||
result = np.zeros(nshape)
|
result = np.zeros(nshape)
|
||||||
cwd = __file__.replace("\\","/") # for windows
|
cwd = __file__.replace("\\","/") # for windows
|
||||||
cwd = cwd.rsplit("/", 1)[0] # the current working directory (where this file is)
|
cwd = cwd.rsplit("/", 1)[0] # the current working directory (where this file is)
|
||||||
if len(cwd) == 0:
|
if len(cwd) == 0:
|
||||||
cwd = "."
|
cwd = "."
|
||||||
icon_spritesheet = cwd + "/weather-icons.bmp"
|
icon_spritesheet = cwd + "/weather-icons.bmp"
|
||||||
|
|
||||||
icons = Image.open(icon_spritesheet)
|
icons = Image.open(icon_spritesheet)
|
||||||
icons_full = np.array(icons)
|
icons_full = np.array(icons)
|
||||||
|
|
||||||
icon_loc = ["sun","moon","sun and clouds", "moon and clouds", "cloud","fog and clouds","2 clouds", "3 clouds", "rain and cloud", "rain and clouds", "rain and cloud and sun", "rain and cloud and moon", "thunder and cloud", "thunder and cloud and moon", "snow and cloud", "snow and cloud and moon", "fog","fog night"]
|
icon_loc = ["sun","moon","sun and clouds", "moon and clouds", "cloud","fog and clouds","2 clouds", "3 clouds", "rain and cloud", "rain and clouds", "rain and cloud and sun", "rain and cloud and moon", "thunder and cloud", "thunder and cloud and moon", "snow and cloud", "snow and cloud and moon", "fog","fog night"]
|
||||||
#ordered 1 2 \n 3 4 \ 5 5 ...
|
#ordered 1 2 \n 3 4 \ 5 5 ...
|
||||||
if name == "":
|
if name == "":
|
||||||
name = "error"
|
name = "error"
|
||||||
name = weather_categories[name]
|
name = weather_categories[name]
|
||||||
try:
|
try:
|
||||||
iy, ix = int(icon_loc.index(name)/2), icon_loc.index(name)%2
|
iy, ix = int(icon_loc.index(name)/2), icon_loc.index(name)%2
|
||||||
# x and y coords
|
# x and y coords
|
||||||
except:
|
except:
|
||||||
return np.zeros((*nshape,3))
|
return np.zeros((*nshape,3))
|
||||||
|
|
||||||
icon_single = icons_full[16*iy:16*(iy + 1),16*ix:16*(ix + 1),...]
|
icon_single = icons_full[16*iy:16*(iy + 1),16*ix:16*(ix + 1),...]
|
||||||
return icon_single
|
return icon_single
|
||||||
|
|
||||||
|
|
||||||
def matrix_add_depth(self, matrix, colors = []):
|
def matrix_add_depth(self, matrix, colors = []):
|
||||||
"""transforms a 2d-array with 0,1,2 to a 3d-array with the rgb values for primary and secondary color"""
|
"""transforms a 2d-array with 0,1,2 to a 3d-array with the rgb values for primary and secondary color"""
|
||||||
|
|
||||||
c1 = self.primary
|
c1 = self.primary
|
||||||
c2 = self.secondary
|
c2 = self.secondary
|
||||||
c3 = self.error
|
c3 = self.error
|
||||||
if len(colors) > 0:
|
if len(colors) > 0:
|
||||||
c1 = colors[0]
|
c1 = colors[0]
|
||||||
if len(colors) > 1:
|
if len(colors) > 1:
|
||||||
c2 = colors[1]
|
c2 = colors[1]
|
||||||
if len(colors) > 2:
|
if len(colors) > 2:
|
||||||
c3 = colors[2]
|
c3 = colors[2]
|
||||||
if len(colors) > 3:
|
if len(colors) > 3:
|
||||||
print("Too many colors")
|
print("Too many colors")
|
||||||
|
|
||||||
r3 = np.zeros((matrix.shape[0],matrix.shape[1],3),dtype=int)
|
r3 = np.zeros((matrix.shape[0],matrix.shape[1],3),dtype=int)
|
||||||
for i in range(matrix.shape[0]):
|
for i in range(matrix.shape[0]):
|
||||||
for j in range(matrix.shape[1]):
|
for j in range(matrix.shape[1]):
|
||||||
t = int(matrix[i, j])
|
t = int(matrix[i, j])
|
||||||
if t == 0:
|
if t == 0:
|
||||||
r3[i, j, :] = [0,0,0]
|
r3[i, j, :] = [0,0,0]
|
||||||
elif t == 1:
|
elif t == 1:
|
||||||
r3[i, j, :] = c1
|
r3[i, j, :] = c1
|
||||||
elif t == 2:
|
elif t == 2:
|
||||||
r3[i, j, :] = c2
|
r3[i, j, :] = c2
|
||||||
else:
|
else:
|
||||||
r3[i, j, :] = c3
|
r3[i, j, :] = c3
|
||||||
return r3
|
return r3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def clock_face(self, weather):
|
def clock_face(self, weather):
|
||||||
"""weather as a dict"""
|
"""weather as a dict"""
|
||||||
hour = self.time_converter()
|
hour = self.time_converter()
|
||||||
day = self.date_converter()
|
day = self.date_converter()
|
||||||
face1 = hour + day
|
face1 = hour + day
|
||||||
# time + date:
|
# time + date:
|
||||||
face1_3d = self.matrix_add_depth(face1)
|
face1_3d = self.matrix_add_depth(face1)
|
||||||
# weather icons
|
# weather icons
|
||||||
face2_3d = self.weather_converter(weather["weather"])
|
face2_3d = self.weather_converter(weather["weather"])
|
||||||
# weather temps
|
# weather temps
|
||||||
face3 = self.time_converter(top=str(weather["low"]), bottom=str(weather["high"]))
|
face3 = self.time_converter(top=str(weather["low"]), bottom=str(weather["high"]))
|
||||||
face3 = np.concatenate((face3[:8,...],2*face3[8:,...]))
|
face3 = np.concatenate((face3[:8,...],2*face3[8:,...]))
|
||||||
face3_3d = self.matrix_add_depth(face3,[[0, 102, 255],[255, 102, 0]])
|
face3_3d = self.matrix_add_depth(face3,[[0, 102, 255],[255, 102, 0]])
|
||||||
|
|
||||||
return [face1_3d, face2_3d, face3_3d]
|
return [face1_3d, face2_3d, face3_3d]
|
||||||
|
|
||||||
|
|
||||||
def text_converter(self, text, height, color):
|
def text_converter(self, text, height, color):
|
||||||
"""Converts a text to a pixel-matrix
|
"""Converts a text to a pixel-matrix
|
||||||
returns: np.array((16, x, 3))"""
|
returns: np.array((16, x, 3))"""
|
||||||
|
|
||||||
font = ImageFont.truetype("verdanab.ttf", height)
|
font = ImageFont.truetype("verdanab.ttf", height)
|
||||||
size = font.getsize(text)
|
size = font.getsize(text)
|
||||||
img = Image.new("1",size,"black")
|
img = Image.new("1",size,"black")
|
||||||
draw = ImageDraw.Draw(img)
|
draw = ImageDraw.Draw(img)
|
||||||
draw.text((0, 0), text, "white", font=font)
|
draw.text((0, 0), text, "white", font=font)
|
||||||
pixels = np.array(img, dtype=np.uint8)
|
pixels = np.array(img, dtype=np.uint8)
|
||||||
pixels3d = self.matrix_add_depth(pixels, color)
|
pixels3d = self.matrix_add_depth(pixels, color)
|
||||||
return pixels3d
|
return pixels3d
|
||||||
|
|
||||||
|
|
||||||
def get_fallback(self):
|
def get_fallback(self):
|
||||||
hour = self.time_converter()
|
hour = self.time_converter()
|
||||||
day = self.date_converter()
|
day = self.date_converter()
|
||||||
face1 = hour + day
|
face1 = hour + day
|
||||||
face1_3d = self.matrix_add_depth(face1)
|
face1_3d = self.matrix_add_depth(face1)
|
||||||
|
|
||||||
face2_3d = face3_3d = np.zeros((16,16,3))
|
face2_3d = face3_3d = np.zeros((16,16,3))
|
||||||
return [face1_3d, face2_3d, face3_3d]
|
return [face1_3d, face2_3d, face3_3d]
|
@ -1,103 +1,103 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
digits = {
|
digits = {
|
||||||
"1" : np.array([
|
"1" : np.array([
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1]]),
|
[0,0,1]]),
|
||||||
"2" : np.array([
|
"2" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,0],
|
[1,0,0],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"3" : np.array([
|
"3" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"4" : np.array([
|
"4" : np.array([
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1]]),
|
[0,0,1]]),
|
||||||
"5" : np.array([
|
"5" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,0],
|
[1,0,0],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"6" : np.array([
|
"6" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,0],
|
[1,0,0],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"7" : np.array([
|
"7" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1]]),
|
[0,0,1]]),
|
||||||
"8" : np.array([
|
"8" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"9" : np.array([
|
"9" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"0" : np.array([
|
"0" : np.array([
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,1,1]]),
|
[1,1,1]]),
|
||||||
"-" : np.array([
|
"-" : np.array([
|
||||||
[0,0,0],
|
[0,0,0],
|
||||||
[0,0,0],
|
[0,0,0],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,0],
|
[0,0,0],
|
||||||
[0,0,0]]),
|
[0,0,0]]),
|
||||||
"-1" : np.array([
|
"-1" : np.array([
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[1,1,1],
|
[1,1,1],
|
||||||
[0,0,1],
|
[0,0,1],
|
||||||
[0,0,1]]),
|
[0,0,1]]),
|
||||||
"error" : np.array([
|
"error" : np.array([
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[0,1,0],
|
[0,1,0],
|
||||||
[1,0,1],
|
[1,0,1],
|
||||||
[1,0,1]]),
|
[1,0,1]]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
weather_categories = {
|
weather_categories = {
|
||||||
"Clouds": "cloud",
|
"Clouds": "cloud",
|
||||||
"Rain": "rain and cloud",
|
"Rain": "rain and cloud",
|
||||||
"Thunderstorm": "thunder and cloud",
|
"Thunderstorm": "thunder and cloud",
|
||||||
"Drizzle": "rain and cloud",
|
"Drizzle": "rain and cloud",
|
||||||
"Snow": "snow and cloud",
|
"Snow": "snow and cloud",
|
||||||
"Clear": "sun",
|
"Clear": "sun",
|
||||||
"Mist": "fog and clouds",
|
"Mist": "fog and clouds",
|
||||||
"Smoke": "Smoke",
|
"Smoke": "Smoke",
|
||||||
"Haze": "Haze",
|
"Haze": "Haze",
|
||||||
"Dust": "Dust",
|
"Dust": "Dust",
|
||||||
"Fog": "fog",
|
"Fog": "fog",
|
||||||
"Sand": "Sand",
|
"Sand": "Sand",
|
||||||
"Dust": "Dust",
|
"Dust": "Dust",
|
||||||
"Ash": "Ash",
|
"Ash": "Ash",
|
||||||
"Squal": "Squal",
|
"Squal": "Squal",
|
||||||
"Tornado": "Tornado",
|
"Tornado": "Tornado",
|
||||||
"error" : "moon"
|
"error" : "moon"
|
||||||
}
|
}
|
@ -1,30 +1,30 @@
|
|||||||
from threading import Timer
|
from threading import Timer
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class RepeatedTimer(object):
|
class RepeatedTimer(object):
|
||||||
def __init__(self, interval, function, *args, **kwargs):
|
def __init__(self, interval, function, *args, **kwargs):
|
||||||
self._timer = None
|
self._timer = None
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.function = function
|
self.function = function
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
self.next_call = time.time()
|
self.next_call = time.time()
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
self.start()
|
self.start()
|
||||||
self.function(*self.args, **self.kwargs)
|
self.function(*self.args, **self.kwargs)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.is_running:
|
if not self.is_running:
|
||||||
self.next_call += self.interval
|
self.next_call += self.interval
|
||||||
self._timer = Timer(self.next_call - time.time(), self._run)
|
self._timer = Timer(self.next_call - time.time(), self._run)
|
||||||
self._timer.start()
|
self._timer.start()
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._timer.cancel()
|
self._timer.cancel()
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
|
@ -1 +1 @@
|
|||||||
# Placeholder
|
# Placeholder
|
||||||
|
@ -1,299 +1,299 @@
|
|||||||
import dash
|
import dash
|
||||||
import dash_bootstrap_components as dbc
|
import dash_bootstrap_components as dbc
|
||||||
import dash_html_components as html
|
import dash_html_components as html
|
||||||
import dash_core_components as dcc
|
import dash_core_components as dcc
|
||||||
import plotly.graph_objects as go
|
import plotly.graph_objects as go
|
||||||
from dash.dependencies import Input, Output
|
from dash.dependencies import Input, Output
|
||||||
|
|
||||||
import locale
|
import locale
|
||||||
locale.setlocale(locale.LC_TIME, "de_DE.utf8")
|
locale.setlocale(locale.LC_TIME, "de_DE.utf8")
|
||||||
#from dash.dependencies import Input, Output
|
#from dash.dependencies import Input, Output
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
class DashBoard():
|
class DashBoard():
|
||||||
""""""
|
""""""
|
||||||
# added by the launcher, we have self.modules (dict)
|
# added by the launcher, we have self.modules (dict)
|
||||||
|
|
||||||
def __init__(self, port):
|
def __init__(self, port):
|
||||||
## pre-sets
|
## pre-sets
|
||||||
|
|
||||||
self.inter_margin = "1em"
|
self.inter_margin = "1em"
|
||||||
self.host_ip = "0.0.0.0"
|
self.host_ip = "0.0.0.0"
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
ex_css = [dbc.themes.BOOTSTRAP]
|
ex_css = [dbc.themes.BOOTSTRAP]
|
||||||
self.app = dash.Dash(__name__, external_stylesheets=ex_css)
|
self.app = dash.Dash(__name__, external_stylesheets=ex_css)
|
||||||
self.app.layout = html.Div([
|
self.app.layout = html.Div([
|
||||||
html.Div(id = 'layout-update', className = "content", style={"padding":self.inter_margin},),
|
html.Div(id = 'layout-update', className = "content", style={"padding":self.inter_margin},),
|
||||||
dcc.Interval(
|
dcc.Interval(
|
||||||
id='interval-component',
|
id='interval-component',
|
||||||
interval=3600*1000, # in milliseconds
|
interval=3600*1000, # in milliseconds
|
||||||
n_intervals=0
|
n_intervals=0
|
||||||
)
|
)
|
||||||
]#,style={'background-image':'url("static/background.jpg")'}
|
]#,style={'background-image':'url("static/background.jpg")'}
|
||||||
)
|
)
|
||||||
|
|
||||||
@self.app.callback(Output('layout-update','children'), Input('interval-component','n_intervals'))
|
@self.app.callback(Output('layout-update','children'), Input('interval-component','n_intervals'))
|
||||||
def update_layout(n):
|
def update_layout(n):
|
||||||
self.set_stats()
|
self.set_stats()
|
||||||
kids = [
|
kids = [
|
||||||
self.card_header(),
|
self.card_header(),
|
||||||
dbc.CardColumns([
|
dbc.CardColumns([
|
||||||
# self.card_weather(),
|
# self.card_weather(),
|
||||||
*self.cards_lists(),
|
*self.cards_lists(),
|
||||||
self.card_bot_stats(),
|
self.card_bot_stats(),
|
||||||
self.card_news(),
|
self.card_news(),
|
||||||
self.card_xkcd(),
|
self.card_xkcd(),
|
||||||
self.card_sensor_stats(),
|
self.card_sensor_stats(),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
return kids
|
return kids
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
flaskThread = Thread(target=app.run_server, kwargs={"host": self.host_ip, "port": self.port}).start()
|
flaskThread = Thread(target=app.run_server, kwargs={"host": self.host_ip, "port": self.port}).start()
|
||||||
#self.app.run_server()#, debug=True)
|
#self.app.run_server()#, debug=True)
|
||||||
|
|
||||||
|
|
||||||
def card_header(self):
|
def card_header(self):
|
||||||
today = datetime.date.today().strftime("%A, der %d. %B %Y")
|
today = datetime.date.today().strftime("%A, der %d. %B %Y")
|
||||||
card = dbc.Card(
|
card = dbc.Card(
|
||||||
[ dbc.CardImg(src="static/header.jpg", top=True, bottom=False,
|
[ dbc.CardImg(src="static/header.jpg", top=True, bottom=False,
|
||||||
title="Header", alt='Header image'),
|
title="Header", alt='Header image'),
|
||||||
dbc.CardBody([html.H3(today, className="card-title")]),
|
dbc.CardBody([html.H3(today, className="card-title")]),
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
style = {"width" : "100%", "margin-bottom":self.inter_margin},
|
style = {"width" : "100%", "margin-bottom":self.inter_margin},
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
def cards_lists(self):
|
def cards_lists(self):
|
||||||
ret = []
|
ret = []
|
||||||
for l in self.persistence["global"]["lists"].keys():
|
for l in self.persistence["global"]["lists"].keys():
|
||||||
l_content = self.persistence["global"]["lists"][l]
|
l_content = self.persistence["global"]["lists"][l]
|
||||||
html_content = [html.A(t, href="#", className="list-group-item bg-dark list-group-item-action text-light") for t in l_content]
|
html_content = [html.A(t, href="#", className="list-group-item bg-dark list-group-item-action text-light") for t in l_content]
|
||||||
card = dbc.Card(
|
card = dbc.Card(
|
||||||
[
|
[
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4("Liste '" + l + "':", className="card-title"),
|
html.H4("Liste '" + l + "':", className="card-title"),
|
||||||
dbc.ListGroup(html_content, flush=True, style={"color":"black"})
|
dbc.ListGroup(html_content, flush=True, style={"color":"black"})
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
ret.append(card)
|
ret.append(card)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def card_bot_stats(self):
|
def card_bot_stats(self):
|
||||||
if not self.stat_graph:
|
if not self.stat_graph:
|
||||||
self.set_stats()
|
self.set_stats()
|
||||||
|
|
||||||
card = dbc.Card(
|
card = dbc.Card(
|
||||||
[
|
[
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4("Chat-Metriken", className="card-title"),
|
html.H4("Chat-Metriken", className="card-title"),
|
||||||
dcc.Graph(figure=self.stat_graph, config={'displayModeBar': False})
|
dcc.Graph(figure=self.stat_graph, config={'displayModeBar': False})
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
def card_sensor_stats(self):
|
def card_sensor_stats(self):
|
||||||
fig = go.Figure()
|
fig = go.Figure()
|
||||||
sensors = self.persistence["clock"]["sensors"]
|
sensors = self.persistence["clock"]["sensors"]
|
||||||
time = sensors["time"]
|
time = sensors["time"]
|
||||||
time = [t - time[0] for t in time] # rescale
|
time = [t - time[0] for t in time] # rescale
|
||||||
for sensor in sensors.keys():
|
for sensor in sensors.keys():
|
||||||
if sensor != "time":
|
if sensor != "time":
|
||||||
fig.add_trace(go.Scatter(x=time, y=sensors[sensor], mode="lines", text=sensor, line=dict(width=4)))
|
fig.add_trace(go.Scatter(x=time, y=sensors[sensor], mode="lines", text=sensor, line=dict(width=4)))
|
||||||
|
|
||||||
fig.layout.update(
|
fig.layout.update(
|
||||||
# xaxis = {
|
# xaxis = {
|
||||||
# 'showgrid': False, # thin lines in the background
|
# 'showgrid': False, # thin lines in the background
|
||||||
# 'zeroline': False, # thick line at x=0
|
# 'zeroline': False, # thick line at x=0
|
||||||
# 'visible': False, # numbers below
|
# 'visible': False, # numbers below
|
||||||
# }, # the same for yaxis
|
# }, # the same for yaxis
|
||||||
# yaxis = {
|
# yaxis = {
|
||||||
# 'showgrid': False, # thin lines in the background
|
# 'showgrid': False, # thin lines in the background
|
||||||
# 'zeroline': False, # thick line at x=0
|
# 'zeroline': False, # thick line at x=0
|
||||||
# 'visible': False, # numbers below
|
# 'visible': False, # numbers below
|
||||||
# }, # the same for yaxis
|
# }, # the same for yaxis
|
||||||
|
|
||||||
showlegend=False,
|
showlegend=False,
|
||||||
# margin=dict(l=0, r=0, t=0, b=0),
|
# margin=dict(l=0, r=0, t=0, b=0),
|
||||||
# paper_bgcolor='rgba(0,0,0,0)',
|
# paper_bgcolor='rgba(0,0,0,0)',
|
||||||
# plot_bgcolor='rgba(0,0,0,0)',
|
# plot_bgcolor='rgba(0,0,0,0)',
|
||||||
)
|
)
|
||||||
|
|
||||||
card = dbc.Card(
|
card = dbc.Card(
|
||||||
[
|
[
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4("Sensor-Metriken", className="card-title"),
|
html.H4("Sensor-Metriken", className="card-title"),
|
||||||
dcc.Graph(figure=fig, config={'displayModeBar': False})
|
dcc.Graph(figure=fig, config={'displayModeBar': False})
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
def card_weather(self):
|
def card_weather(self):
|
||||||
def weather_item(name, overview, temps):
|
def weather_item(name, overview, temps):
|
||||||
if len(temps) == 2:
|
if len(temps) == 2:
|
||||||
temp = "🌡(❄): " + str(temps[0]) + "° ➡ 🌡(🔥): " + str(temps[1]) + "°"
|
temp = "🌡(❄): " + str(temps[0]) + "° ➡ 🌡(🔥): " + str(temps[1]) + "°"
|
||||||
else:
|
else:
|
||||||
temp = "🌡: " + str(temps[0]) + "°"
|
temp = "🌡: " + str(temps[0]) + "°"
|
||||||
temp_line = html.P(temp, className="mb-1")
|
temp_line = html.P(temp, className="mb-1")
|
||||||
|
|
||||||
it = html.A([
|
it = html.A([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H5(name, className="mb-1"),
|
html.H5(name, className="mb-1"),
|
||||||
html.Span(categories[overview], className="badge badge-primary badge-pill")
|
html.Span(categories[overview], className="badge badge-primary badge-pill")
|
||||||
],
|
],
|
||||||
className="d-flex w-100 justify-content-between"),
|
className="d-flex w-100 justify-content-between"),
|
||||||
temp_line,
|
temp_line,
|
||||||
],
|
],
|
||||||
href="#", className="list-group-item bg-dark list-group-item-action text-light"
|
href="#", className="list-group-item bg-dark list-group-item-action text-light"
|
||||||
)
|
)
|
||||||
|
|
||||||
return it
|
return it
|
||||||
|
|
||||||
days = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
|
days = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
|
||||||
categories = {"Clouds": "☁", "Rain": "🌧", "Thunderstorm": "🌩", "Drizzle": ":droplet:", "Snow": "❄", "Clear": "☀", "Mist": "🌫", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
categories = {"Clouds": "☁", "Rain": "🌧", "Thunderstorm": "🌩", "Drizzle": ":droplet:", "Snow": "❄", "Clear": "☀", "Mist": "🌫", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
|
||||||
today = datetime.datetime.today().weekday()
|
today = datetime.datetime.today().weekday()
|
||||||
|
|
||||||
body = []
|
body = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bot = self.modules["bot"]
|
bot = self.modules["bot"]
|
||||||
content = bot.api_weather.show_weather([47.3769, 8.5417]) # still zürich
|
content = bot.api_weather.show_weather([47.3769, 8.5417]) # still zürich
|
||||||
|
|
||||||
wt = content.pop(0)
|
wt = content.pop(0)
|
||||||
body.append(weather_item("Jetzt", wt["short"], wt["temps"]))
|
body.append(weather_item("Jetzt", wt["short"], wt["temps"]))
|
||||||
|
|
||||||
for i, day in enumerate(content):
|
for i, day in enumerate(content):
|
||||||
tmp = []
|
tmp = []
|
||||||
if i == 0:
|
if i == 0:
|
||||||
day_name = "Heute"
|
day_name = "Heute"
|
||||||
else:
|
else:
|
||||||
day_name = days[(today + i) % 7]
|
day_name = days[(today + i) % 7]
|
||||||
|
|
||||||
body.append(weather_item(day_name, day["short"], day["temps"]))
|
body.append(weather_item(day_name, day["short"], day["temps"]))
|
||||||
body = dbc.ListGroup(body, flush=True, style={"color":"black"})
|
body = dbc.ListGroup(body, flush=True, style={"color":"black"})
|
||||||
|
|
||||||
|
|
||||||
except:
|
except:
|
||||||
body.append(html.H6("Konnte nicht geladen werden"))
|
body.append(html.H6("Konnte nicht geladen werden"))
|
||||||
|
|
||||||
card = dbc.Card(
|
card = dbc.Card(
|
||||||
[dbc.CardBody([
|
[dbc.CardBody([
|
||||||
html.H4("Wetter", className="card-title"),
|
html.H4("Wetter", className="card-title"),
|
||||||
body])],
|
body])],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
def card_news(self):
|
def card_news(self):
|
||||||
try:
|
try:
|
||||||
card = dbc.Card([
|
card = dbc.Card([
|
||||||
dbc.CardBody([html.Iframe(src="https://nzz.ch", style={"border":"none", "min-height":"30em", "width":"100%"})])
|
dbc.CardBody([html.Iframe(src="https://nzz.ch", style={"border":"none", "min-height":"30em", "width":"100%"})])
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
card = card = dbc.Card([
|
card = card = dbc.Card([
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4("Could not load NEWS", className="card-title"),
|
html.H4("Could not load NEWS", className="card-title"),
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
def card_xkcd(self):
|
def card_xkcd(self):
|
||||||
try:
|
try:
|
||||||
xml = requests.get("https://xkcd.com/atom.xml").content
|
xml = requests.get("https://xkcd.com/atom.xml").content
|
||||||
feed = xmltodict.parse(xml)
|
feed = xmltodict.parse(xml)
|
||||||
title = feed["feed"]["entry"][0]["title"]
|
title = feed["feed"]["entry"][0]["title"]
|
||||||
img = feed["feed"]["entry"][0]["summary"]["#text"]
|
img = feed["feed"]["entry"][0]["summary"]["#text"]
|
||||||
i1 = img.find('"') +1
|
i1 = img.find('"') +1
|
||||||
i2 = img.find('"', i1+1)
|
i2 = img.find('"', i1+1)
|
||||||
i3 = img.find('"', i2+1) + 1
|
i3 = img.find('"', i2+1) + 1
|
||||||
i4 = img.find('"', i3+1)
|
i4 = img.find('"', i3+1)
|
||||||
img_src = img[i1:i2]
|
img_src = img[i1:i2]
|
||||||
img_alt = img[i3:i4]
|
img_alt = img[i3:i4]
|
||||||
card = dbc.Card([
|
card = dbc.Card([
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4(title, className="card-title"),
|
html.H4(title, className="card-title"),
|
||||||
html.Img(src=img_src, style={"width":"100%"}),
|
html.Img(src=img_src, style={"width":"100%"}),
|
||||||
html.P(img_alt)
|
html.P(img_alt)
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
card = dbc.Card([
|
card = dbc.Card([
|
||||||
dbc.CardBody([
|
dbc.CardBody([
|
||||||
html.H4("Could not load XKCD", className="card-title"),
|
html.H4("Could not load XKCD", className="card-title"),
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
color="dark",
|
color="dark",
|
||||||
inverse=True,
|
inverse=True,
|
||||||
)
|
)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
|
||||||
######### helper:
|
######### helper:
|
||||||
def set_stats(self):
|
def set_stats(self):
|
||||||
xs, ys = helpers.clean_axis(self.persistence["bot"]["send_activity"]["hour"], self.persistence["bot"]["send_activity"]["count"])
|
xs, ys = helpers.clean_axis(self.persistence["bot"]["send_activity"]["hour"], self.persistence["bot"]["send_activity"]["count"])
|
||||||
xr, yr = helpers.clean_axis(self.persistence["bot"]["receive_activity"]["hour"], self.persistence["bot"]["receive_activity"]["count"])
|
xr, yr = helpers.clean_axis(self.persistence["bot"]["receive_activity"]["hour"], self.persistence["bot"]["receive_activity"]["count"])
|
||||||
xe, ye = helpers.clean_axis(self.persistence["bot"]["execute_activity"]["hour"], self.persistence["bot"]["execute_activity"]["count"])
|
xe, ye = helpers.clean_axis(self.persistence["bot"]["execute_activity"]["hour"], self.persistence["bot"]["execute_activity"]["count"])
|
||||||
|
|
||||||
fig = go.Figure()
|
fig = go.Figure()
|
||||||
fig.add_trace(go.Scatter(x=xr, y=yr, mode="lines", text="Gelesen", line=dict(width=4)))
|
fig.add_trace(go.Scatter(x=xr, y=yr, mode="lines", text="Gelesen", line=dict(width=4)))
|
||||||
fig.add_trace(go.Scatter(x=xs, y=ys, mode="lines", text="Gesendet", line=dict(width=4)))
|
fig.add_trace(go.Scatter(x=xs, y=ys, mode="lines", text="Gesendet", line=dict(width=4)))
|
||||||
fig.add_trace(go.Scatter(x=xe, y=ye, mode="lines", text="Ausgeführt", line=dict(width=4)))
|
fig.add_trace(go.Scatter(x=xe, y=ye, mode="lines", text="Ausgeführt", line=dict(width=4)))
|
||||||
|
|
||||||
fig.update_xaxes(showgrid=False)
|
fig.update_xaxes(showgrid=False)
|
||||||
fig.update_yaxes(showgrid=False)
|
fig.update_yaxes(showgrid=False)
|
||||||
fig.layout.update(
|
fig.layout.update(
|
||||||
xaxis = {
|
xaxis = {
|
||||||
'showgrid': False, # thin lines in the background
|
'showgrid': False, # thin lines in the background
|
||||||
'zeroline': False, # thick line at x=0
|
'zeroline': False, # thick line at x=0
|
||||||
'visible': False, # numbers below
|
'visible': False, # numbers below
|
||||||
}, # the same for yaxis
|
}, # the same for yaxis
|
||||||
yaxis = {
|
yaxis = {
|
||||||
'showgrid': False, # thin lines in the background
|
'showgrid': False, # thin lines in the background
|
||||||
'zeroline': False, # thick line at x=0
|
'zeroline': False, # thick line at x=0
|
||||||
'visible': False, # numbers below
|
'visible': False, # numbers below
|
||||||
}, # the same for yaxis
|
}, # the same for yaxis
|
||||||
|
|
||||||
showlegend=False,
|
showlegend=False,
|
||||||
margin=dict(l=0, r=0, t=0, b=0),
|
margin=dict(l=0, r=0, t=0, b=0),
|
||||||
paper_bgcolor='rgba(0,0,0,0)',
|
paper_bgcolor='rgba(0,0,0,0)',
|
||||||
plot_bgcolor='rgba(0,0,0,0)',
|
plot_bgcolor='rgba(0,0,0,0)',
|
||||||
)
|
)
|
||||||
|
|
||||||
self.stat_graph = fig
|
self.stat_graph = fig
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
|
|
||||||
def clean_axis(x,y):
|
def clean_axis(x,y):
|
||||||
"""x is the time the point in y was taken"""
|
"""x is the time the point in y was taken"""
|
||||||
try:
|
try:
|
||||||
xn = range(x[0], x[-1]+1)
|
xn = range(x[0], x[-1]+1)
|
||||||
yn = []
|
yn = []
|
||||||
count = 0
|
count = 0
|
||||||
for x_i in xn:
|
for x_i in xn:
|
||||||
if x_i in x:
|
if x_i in x:
|
||||||
yn.append(y[count])
|
yn.append(y[count])
|
||||||
count += 1
|
count += 1
|
||||||
else:
|
else:
|
||||||
yn.append(0)
|
yn.append(0)
|
||||||
xn = [i - int(x[0]) for i in xn]
|
xn = [i - int(x[0]) for i in xn]
|
||||||
except:
|
except:
|
||||||
xn = []
|
xn = []
|
||||||
yn = []
|
yn = []
|
||||||
return xn, yn
|
return xn, yn
|
@ -1,33 +1,33 @@
|
|||||||
div.header {
|
div.header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: var(--heading-height);
|
height: var(--heading-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create angled background with 'before' pseudo-element */
|
/* Create angled background with 'before' pseudo-element */
|
||||||
header::before {
|
header::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 6em;
|
bottom: 6em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(var(--heading-height) + 10em);
|
height: calc(var(--heading-height) + 10em);
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
transform: skewY(-3.5deg);
|
transform: skewY(-3.5deg);
|
||||||
background:
|
background:
|
||||||
linear-gradient(rgba(0,0,0,.6), rgba(0,0,0,.6)),
|
linear-gradient(rgba(0,0,0,.6), rgba(0,0,0,.6)),
|
||||||
url(https://images.unsplash.com/photo-1495464101292-552d0b52fe41?auto=format&fit=crop&w=1350&q=80) no-repeat center,
|
url(https://images.unsplash.com/photo-1495464101292-552d0b52fe41?auto=format&fit=crop&w=1350&q=80) no-repeat center,
|
||||||
linear-gradient(#4e4376, #2b5876);
|
linear-gradient(#4e4376, #2b5876);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-bottom: .2em solid #fff;
|
border-bottom: .2em solid #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: calc(2.8em + 2.6vw);
|
font-size: calc(2.8em + 2.6vw);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
letter-spacing: .01em;
|
letter-spacing: .01em;
|
||||||
padding: 6rem 0 0 4.5rem;
|
padding: 6rem 0 0 4.5rem;
|
||||||
text-shadow: .022em .022em .022em #111;
|
text-shadow: .022em .022em .022em #111;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
146
launcher.py
146
launcher.py
@ -1,73 +1,73 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from persistence import p_io, p_out
|
from persistence import p_io, p_out
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Launcher:
|
class Launcher:
|
||||||
"""base launcher that launches other submodules"""
|
"""base launcher that launches other submodules"""
|
||||||
|
|
||||||
def __init__(self, **modules):
|
def __init__(self, **modules):
|
||||||
""""""
|
""""""
|
||||||
self.persistence = p_io.PersistentDict("persistence/prst.json")
|
self.persistence = p_io.PersistentDict("persistence/prst.json")
|
||||||
self.db = p_out.DataBaseConnector()
|
self.db = p_out.DataBaseConnector()
|
||||||
|
|
||||||
logger.info(self.__class__.__name__ + " initialized")
|
logger.info(self.__class__.__name__ + " initialized")
|
||||||
|
|
||||||
self.modules = modules
|
self.modules = modules
|
||||||
if len(self.persistence) == 0:
|
if len(self.persistence) == 0:
|
||||||
self.init_persistence()
|
self.init_persistence()
|
||||||
self.persistence["global"]["reboots"] += 1
|
self.persistence["global"]["reboots"] += 1
|
||||||
|
|
||||||
self.launch_modules()
|
self.launch_modules()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def launch_modules(self):
|
def launch_modules(self):
|
||||||
|
|
||||||
for module in self.modules.values():
|
for module in self.modules.values():
|
||||||
logger.info("Starting module "+ module.__class__.__name__)
|
logger.info("Starting module "+ module.__class__.__name__)
|
||||||
module.modules = self.modules
|
module.modules = self.modules
|
||||||
module.persistence = self.persistence
|
module.persistence = self.persistence
|
||||||
module.db = self.db # pooled ie multithreaded
|
module.db = self.db # pooled ie multithreaded
|
||||||
module.start()
|
module.start()
|
||||||
|
|
||||||
|
|
||||||
def init_persistence(self):
|
def init_persistence(self):
|
||||||
logger.warning("No persistence found, created a new one")
|
logger.warning("No persistence found, created a new one")
|
||||||
|
|
||||||
self.persistence["global"] ={
|
self.persistence["global"] ={
|
||||||
"lists" : {},
|
"lists" : {},
|
||||||
"reboots": 0
|
"reboots": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for m_name in self.modules.keys():
|
for m_name in self.modules.keys():
|
||||||
data = {}
|
data = {}
|
||||||
if m_name == "bot":
|
if m_name == "bot":
|
||||||
data = {
|
data = {
|
||||||
"send_activity" : {"hour":[], "count":[]},
|
"send_activity" : {"hour":[], "count":[]},
|
||||||
"receive_activity" : {"hour":[], "count":[]},
|
"receive_activity" : {"hour":[], "count":[]},
|
||||||
"execute_activity" : {"hour":[], "count":[]},
|
"execute_activity" : {"hour":[], "count":[]},
|
||||||
"log": [],
|
"log": [],
|
||||||
"chat_members": {},
|
"chat_members": {},
|
||||||
"aliases" : {}
|
"aliases" : {}
|
||||||
}
|
}
|
||||||
if m_name == "clock":
|
if m_name == "clock":
|
||||||
data = {
|
data = {
|
||||||
"sensors" : {
|
"sensors" : {
|
||||||
"time" : [],
|
"time" : [],
|
||||||
"temperature":[],
|
"temperature":[],
|
||||||
"humidity":[],
|
"humidity":[],
|
||||||
"brightness" : [],
|
"brightness" : [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.persistence[m_name] = data
|
self.persistence[m_name] = data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Aand liftoff!
|
## Aand liftoff!
|
||||||
# Launcher()
|
# Launcher()
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
## What is happening here?
|
## What is happening here?
|
||||||
|
|
||||||
This "persistence"-module aims to standardize 2 things:
|
This "persistence"-module aims to standardize 2 things:
|
||||||
* the creation of a common set of variables that survives a potential (let's face it, likely) crash
|
* the creation of a common set of variables that survives a potential (let's face it, likely) crash
|
||||||
* advanced logging and analytics
|
* advanced logging and analytics
|
||||||
|
|
||||||
### Common variables
|
### Common variables
|
||||||
These are saved as a json file and are handled internally as a dict. Each change in the dict triggers a write to the file.
|
These are saved as a json file and are handled internally as a dict. Each change in the dict triggers a write to the file.
|
||||||
|
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
A chunky sqlite-db which periodically gets new entries. From all modules. Ideally this db is then visualized through grafana. WIP
|
A chunky sqlite-db which periodically gets new entries. From all modules. Ideally this db is then visualized through grafana. WIP
|
@ -1,71 +1,55 @@
|
|||||||
from peewee import *
|
from datetime import datetime
|
||||||
# from playhouse.pool import PooledMySQLDatabase
|
from peewee import *
|
||||||
from playhouse.shortcuts import ReconnectMixin
|
import logging
|
||||||
import logging
|
import json
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
db = DatabaseProxy()
|
def create_tables(db):
|
||||||
# set the nature of the db at runtime
|
db.create_tables([SensorMetric, ChatMetric, ErrorMetric, List])
|
||||||
|
|
||||||
class ReconnectDataBase(ReconnectMixin, MySQLDatabase):
|
db = DatabaseProxy()
|
||||||
pass
|
# set the nature of the db at runtime
|
||||||
|
|
||||||
|
|
||||||
class DBModel(Model):
|
class DBModel(Model):
|
||||||
# specific to the above DB
|
# specific to the above DB
|
||||||
class Meta:
|
class Meta:
|
||||||
database = db
|
database = db
|
||||||
|
|
||||||
def save(self):
|
class Metric(DBModel):
|
||||||
# fail-safe writing of the db-object. Usually threaded because the caller is threaded
|
time = DateTimeField(default = datetime.now())
|
||||||
try:
|
|
||||||
# db.connect()
|
|
||||||
super().save()
|
|
||||||
# db.close()
|
### Actual metrics:
|
||||||
except Exception as e:
|
|
||||||
logger.error("Could not write to db. Dropping content of {}".format(self.__class__.__name__))
|
class SensorMetric(Metric):
|
||||||
logger.error(e)
|
# this is a continuous metric
|
||||||
# db.atomic().rollback()
|
temperature = IntegerField()
|
||||||
|
humidity = IntegerField()
|
||||||
# def get(self, *query, **filters):
|
luminosity = IntegerField()
|
||||||
# try:
|
|
||||||
# return super().get(*query, **filters)
|
|
||||||
# except Exception as e:
|
class ChatMetric(Metric):
|
||||||
# logger.error("Error while executing get: {}".format(e))
|
read = BooleanField()
|
||||||
# print(query, filters)
|
send = BooleanField()
|
||||||
|
execute = BooleanField()
|
||||||
|
|
||||||
class Metric(DBModel):
|
|
||||||
time = DateTimeField()
|
class ErrorMetric(Metric):
|
||||||
|
# same as above
|
||||||
|
error = TextField()
|
||||||
### Actual metrics:
|
|
||||||
|
|
||||||
class SensorMetric(Metric):
|
class List(DBModel):
|
||||||
# this is a continuous metric
|
name = CharField(unique=True)
|
||||||
temperature = IntegerField()
|
content = TextField() # unlimited length, use to serialise list into
|
||||||
humidity = IntegerField()
|
|
||||||
luminosity = IntegerField()
|
@property
|
||||||
default = {"temperature": 100, "humidity": 100, "luminosity": 100}
|
def content_as_list(self):
|
||||||
|
return json.loads(self.content)
|
||||||
|
|
||||||
|
def set_content(self, list_content):
|
||||||
class ChatMetric(Metric):
|
self.content = json.dumps(list_content)
|
||||||
read = BooleanField()
|
|
||||||
send = BooleanField()
|
|
||||||
execute = BooleanField()
|
|
||||||
default = {"read": False, "send": False, "execute": False}
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorMetric(Metric):
|
|
||||||
# same as above
|
|
||||||
error = TextField()
|
|
||||||
default = {"error": "SQL connection broke off"}
|
|
||||||
|
|
||||||
|
|
||||||
class List(DBModel):
|
|
||||||
name = CharField(unique=True)
|
|
||||||
content = TextField() # unlimited length, use to serialise list into
|
|
||||||
default = {"content": "SQL connection broke off"}
|
|
@ -1,87 +1,87 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class PersistentDict(dict):
|
class PersistentDict(dict):
|
||||||
"""Extended dict that writes its content to a file every time a value is changed"""
|
"""Extended dict that writes its content to a file every time a value is changed"""
|
||||||
|
|
||||||
def __init__(self, file_name, *args, **kwargs):
|
def __init__(self, file_name, *args, **kwargs):
|
||||||
"""initialization of the dict and of the required files"""
|
"""initialization of the dict and of the required files"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.path = file_name
|
self.path = file_name
|
||||||
self.last_action = ""
|
self.last_action = ""
|
||||||
try:
|
try:
|
||||||
self.read_dict()
|
self.read_dict()
|
||||||
except:
|
except:
|
||||||
with open(self.path, "a") as f:
|
with open(self.path, "a") as f:
|
||||||
f.write("{}")
|
f.write("{}")
|
||||||
|
|
||||||
|
|
||||||
## helper - functions
|
## helper - functions
|
||||||
def write_dict(self):
|
def write_dict(self):
|
||||||
with open(self.path, "w") as f:
|
with open(self.path, "w") as f:
|
||||||
json.dump(self, f)
|
json.dump(self, f)
|
||||||
self.last_action = "w"
|
self.last_action = "w"
|
||||||
|
|
||||||
def read_dict(self):
|
def read_dict(self):
|
||||||
with open(self.path) as f:
|
with open(self.path) as f:
|
||||||
tmp = dict(json.load(f))
|
tmp = dict(json.load(f))
|
||||||
for key in tmp:
|
for key in tmp:
|
||||||
super().__setitem__(key, tmp[key])
|
super().__setitem__(key, tmp[key])
|
||||||
self.last_action = "r"
|
self.last_action = "r"
|
||||||
|
|
||||||
|
|
||||||
## extended dictionary - logic
|
## extended dictionary - logic
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if self.last_action != "r":
|
if self.last_action != "r":
|
||||||
self.read_dict()
|
self.read_dict()
|
||||||
# not sure if the step to read is necessary, but I'll keep it for safety
|
# not sure if the step to read is necessary, but I'll keep it for safety
|
||||||
super().__setitem__(key,value)
|
super().__setitem__(key,value)
|
||||||
self.write_dict() # writes 'self' to a json file.
|
self.write_dict() # writes 'self' to a json file.
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if self.last_action != "r":
|
if self.last_action != "r":
|
||||||
self.read_dict()
|
self.read_dict()
|
||||||
|
|
||||||
ret_val = super().__getitem__(key)
|
ret_val = super().__getitem__(key)
|
||||||
if type(ret_val) != int and type(ret_val) != str:
|
if type(ret_val) != int and type(ret_val) != str:
|
||||||
ret_val = create_struct(type(ret_val), key, self, ret_val)
|
ret_val = create_struct(type(ret_val), key, self, ret_val)
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
super().clear()
|
super().clear()
|
||||||
self.write_dict()
|
self.write_dict()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_struct(struct_type, own_name, parent_name, *args, **kwargs):
|
def create_struct(struct_type, own_name, parent_name, *args, **kwargs):
|
||||||
class HookedStruct(struct_type):
|
class HookedStruct(struct_type):
|
||||||
|
|
||||||
def __init__(self, own_name, parent_name, *args, **kwargs):
|
def __init__(self, own_name, parent_name, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.name = own_name
|
self.name = own_name
|
||||||
self.parent = parent_name
|
self.parent = parent_name
|
||||||
|
|
||||||
def __setitem__(self, *args, **kwargs):
|
def __setitem__(self, *args, **kwargs):
|
||||||
super().__setitem__(*args, **kwargs)
|
super().__setitem__(*args, **kwargs)
|
||||||
self.parent.__setitem__(self.name, self)
|
self.parent.__setitem__(self.name, self)
|
||||||
|
|
||||||
def __getitem__(self, *args, **kwargs):
|
def __getitem__(self, *args, **kwargs):
|
||||||
ret_val = super().__getitem__(*args, **kwargs)
|
ret_val = super().__getitem__(*args, **kwargs)
|
||||||
if type(ret_val) != int and type(ret_val) != str:
|
if type(ret_val) != int and type(ret_val) != str:
|
||||||
ret_val = create_struct(type(ret_val), args[0], self, ret_val)
|
ret_val = create_struct(type(ret_val), args[0], self, ret_val)
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
def pop(self, *args):
|
def pop(self, *args):
|
||||||
retvalue = super().pop(*args)
|
retvalue = super().pop(*args)
|
||||||
self.parent.__setitem__(self.name, self)
|
self.parent.__setitem__(self.name, self)
|
||||||
return retvalue
|
return retvalue
|
||||||
|
|
||||||
def append(self, *args):
|
def append(self, *args):
|
||||||
super().append(*args)
|
super().append(*args)
|
||||||
self.parent.__setitem__(self.name, self)
|
self.parent.__setitem__(self.name, self)
|
||||||
|
|
||||||
return HookedStruct(own_name, parent_name, *args, **kwargs)
|
return HookedStruct(own_name, parent_name, *args, **kwargs)
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
from . import models
|
|
||||||
from peewee import *
|
|
||||||
# from playhouse.pool import PooledMySQLDatabase
|
|
||||||
from playhouse.shortcuts import ReconnectMixin
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
from . import keys
|
|
||||||
dbk = keys.db_keys
|
|
||||||
|
|
||||||
|
|
||||||
class ReconnectDataBase(ReconnectMixin, MySQLDatabase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DBConnector:
|
|
||||||
"""Create a connection to a remote database and log some quantities that will be visualized otherwhere"""
|
|
||||||
def __init__(self):
|
|
||||||
self.db = models.db
|
|
||||||
|
|
||||||
self.sensors = models.SensorMetric
|
|
||||||
self.chats = models.ChatMetric
|
|
||||||
self.errors = models.ErrorMetric
|
|
||||||
self.lists = models.List
|
|
||||||
|
|
||||||
self.create_tables()
|
|
||||||
|
|
||||||
def create_tables(self):
|
|
||||||
self.db.create_tables([self.sensors, self.chats, self.errors, self.lists])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DataBaseConnector:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.db_object = models.ReconnectDataBase(
|
|
||||||
dbk["name"],
|
|
||||||
user=dbk["username"],
|
|
||||||
password=dbk["password"],
|
|
||||||
host=dbk["url"],
|
|
||||||
port=dbk["port"],
|
|
||||||
autorollback=True
|
|
||||||
)
|
|
||||||
models.db.initialize(self.db_object)
|
|
||||||
|
|
||||||
# self.sensors = models.SensorMetric
|
|
||||||
# self.chats = models.ChatMetric
|
|
||||||
# self.errors = models.ErrorMetric
|
|
||||||
# self.lists = models.List
|
|
||||||
## Set as property methods instead
|
|
||||||
|
|
||||||
self.db_object.create_tables([self.sensors, self.chats, self.errors, self.lists])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sensors(self):
|
|
||||||
self.connect_first()
|
|
||||||
return models.SensorMetric
|
|
||||||
|
|
||||||
@property
|
|
||||||
def chats(self):
|
|
||||||
self.connect_first()
|
|
||||||
return models.ChatMetric
|
|
||||||
|
|
||||||
@property
|
|
||||||
def errors(self):
|
|
||||||
self.connect_first()
|
|
||||||
return models.ErrorMetric
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lists(self):
|
|
||||||
self.connect_first()
|
|
||||||
return models.List
|
|
||||||
|
|
||||||
def connect_first(self):
|
|
||||||
# if self.db_object.is_closed():
|
|
||||||
# self.db_object.connect()
|
|
||||||
self.db_object.connect(reuse_if_open=True)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def auto_reconnect(func, *args, **kwargs):
|
|
||||||
# return func
|
|
||||||
|
|
||||||
# def classwide_decorator(decorator):
|
|
||||||
# def decorate(cls):
|
|
||||||
# for attr in inspect.getmembers(cls, inspect.ismethod): # there's propably a better way to do this
|
|
||||||
# # TODO: filter init
|
|
||||||
# print(attr)
|
|
||||||
# if callable(getattr(cls, attr)):
|
|
||||||
# setattr(cls, attr, decorator(getattr(cls, attr)))
|
|
||||||
# return cls
|
|
||||||
# return decorate
|
|
||||||
|
|
||||||
# # apply auto_reconnect to every method so that every method first checks the db connection and reconnects if necessary
|
|
||||||
# @classwide_decorator(auto_reconnect)
|
|
||||||
# class DataBaseConnector(ReconnectMixin, MySQLDatabase):
|
|
||||||
# def __init__(self, *args, **kwargs):
|
|
||||||
# super().__init__(
|
|
||||||
# dbk["name"],
|
|
||||||
# user=dbk["username"],
|
|
||||||
# password=dbk["password"],
|
|
||||||
# host=dbk["url"],
|
|
||||||
# port=dbk["port"],
|
|
||||||
# autorollback=True,
|
|
||||||
# *args, **kwargs)
|
|
||||||
|
|
||||||
# models.db.initialize(self)
|
|
||||||
# self.sensors = models.SensorMetric
|
|
||||||
# self.chats = models.ChatMetric
|
|
||||||
# self.errors = models.ErrorMetric
|
|
||||||
# self.lists = models.List
|
|
||||||
|
|
||||||
# self.create_tables([self.sensors, self.chats, self.errors, self.lists])
|
|
||||||
|
|
||||||
# def m1(self): pass
|
|
||||||
# def m2(self, x): pass
|
|
32
req_pi.txt
32
req_pi.txt
@ -1,17 +1,17 @@
|
|||||||
Adafruit-Blinka==6.10.3
|
Adafruit-Blinka==6.10.3
|
||||||
adafruit-circuitpython-dht==3.6.1
|
adafruit-circuitpython-dht==3.6.1
|
||||||
Adafruit-PlatformDetect==3.14.2
|
Adafruit-PlatformDetect==3.14.2
|
||||||
Adafruit-PureIO==1.1.9
|
Adafruit-PureIO==1.1.9
|
||||||
board==1.0
|
board==1.0
|
||||||
importlib-metadata==4.6.0
|
importlib-metadata==4.6.0
|
||||||
pkg-resources==0.0.0
|
pkg-resources==0.0.0
|
||||||
psycopg2==2.9.1
|
psycopg2==2.9.1
|
||||||
psycopg2-binary==2.9.1
|
psycopg2-binary==2.9.1
|
||||||
pyftdi==0.53.1
|
pyftdi==0.53.1
|
||||||
pyserial==3.5
|
pyserial==3.5
|
||||||
pyusb==1.1.1
|
pyusb==1.1.1
|
||||||
rpi-ws281x==4.3.0
|
rpi-ws281x==4.3.0
|
||||||
RPi.GPIO==0.7.0
|
RPi.GPIO==0.7.0
|
||||||
sysv-ipc==1.1.0
|
sysv-ipc==1.1.0
|
||||||
typing-extensions==3.10.0.0
|
typing-extensions==3.10.0.0
|
||||||
zipp==3.4.1
|
zipp==3.4.1
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -1 +0,0 @@
|
|||||||
Subproject commit 1e24c1491d2e80ce9d915b20f434c1e10fab3156
|
|
20
test.py
20
test.py
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
class MyResource:
|
|
||||||
def __enter__(self):
|
|
||||||
print('Entering context.')
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *exc):
|
|
||||||
print('EXITING context.')
|
|
||||||
|
|
||||||
def try_me(self):
|
|
||||||
print("I work")
|
|
||||||
|
|
||||||
def fun():
|
|
||||||
with MyResource() as a:
|
|
||||||
print('Returning inside with-statement.')
|
|
||||||
return a
|
|
||||||
print('Returning outside with-statement.')
|
|
||||||
|
|
||||||
t =fun()
|
|
||||||
t.try_me()
|
|
Loading…
x
Reference in New Issue
Block a user