Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • fejao/c3buttons
1 result
Show changes
Showing
with 751 additions and 0 deletions
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
dotenv
### DJANGO
django
djangorestframework
### SWAGGER
drf-yasg
### TOKEN
djangorestframework-simplejwt
### SSL
django-sslserver
"""
Default setted file from django for an added app.
"""
from django.apps import AppConfig
class SharedModelsConfig(AppConfig):
"""Default setted SharedModelsConfig class for the app.
"""
default_auto_field = "django.db.models.BigAutoField"
name = "shared_models"
# Generated by Django 5.1.6 on 2025-03-03 18:22
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='ButtonBox',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=40, unique=True)),
('pin_button', models.PositiveIntegerField(unique=True)),
('pin_led', models.PositiveIntegerField(unique=True)),
],
options={
'db_table': 'button_box',
},
),
migrations.CreateModel(
name='ButtonBoxUse',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('box_used', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared_models.buttonbox')),
],
options={
'db_table': 'button_box_use',
},
),
]
"""
Serializers for the models shared among this django.
"""
from django.db import models
class ButtonBox(models.Model):
"""The model for storing the information about the button box.
"""
name = models.CharField(max_length=40, unique=True)
# pin_number = models.PositiveIntegerField(unique=True)
pin_button = models.PositiveIntegerField(unique=True)
pin_led = models.PositiveIntegerField(unique=True)
class Meta:
""" Meta class for the Button Box model.
Used for setting the name ot the table at the DB.
"""
db_table = "button_box"
def __str__(self):
"""Override from the default __str__ method for returning the name of the box instead of it's ID.
"""
return str(self.name)
class ButtonBoxUse(models.Model):
"""The model for storing the information about the button box uses.
"""
box_used = models.ForeignKey(
ButtonBox,
on_delete=models.CASCADE,
)
date = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
""" Meta class for the Button Box Use model.
Used for setting the name ot the table at the DB.
"""
db_table = "button_box_use"
@property
def box_name(self):
"""Property method for fetching the name of the box for the box used.
"""
box_obj = ButtonBox.objects.get(name=self.box_used)
return box_obj.name
"""
Serializer used for the used shared models among the apps.
"""
from rest_framework import serializers
from .models import ButtonBox, ButtonBoxUse
class ButtonBoxSerializer(serializers.ModelSerializer):
"""The serializer for the information about the button box.
"""
class Meta:
""" Meta class for the ButtonBoxSerializer.
Used for returning only the information needed for the ButtonBox model.
"""
model = ButtonBox
fields = [
# "id",
"name",
# "pin_number",
"pin_button",
"pin_led",
]
class ButtonBoxUseSerializer(serializers.ModelSerializer):
"""The serializer for the information about the button box uses.
"""
class Meta:
""" Meta class for the ButtonBoxSerializer.
Used for returning only the information needed for the ButtonBoxUse model.
"""
model = ButtonBoxUse
fields = [
# "id",
"box_used",
"box_name",
"date",
]
FROM python:3.10.15-bullseye
# Set variables
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ="Europe/Berlin"
# Install dependencies
RUN apt-get update -qq && apt-get upgrade -qqy \
&& apt-get install -qqy \
python3-pip \
htop \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Create the folder at the container
WORKDIR /c3buttons
# Install python requirements
COPY ../../django/requirements.txt ./
RUN pip3 install -r requirements.txt
# Copy App
COPY ../../django/ .
### TEST
# CMD ["sleep", "10000000"]
### PROD
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
---
services:
django:
image: c3buttons-django:latest
container_name: c3buttons-django
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8000/auth/test/open || exit 1"]
interval: 30s
timeout: 5s
retries: 5
### DISABLE PORTS FOR PRODUCTION
ports:
- '8000:8000'
# volumes:
# - ./django/data/db.sqlite3:/c3buttons/db.sqlite3
environment:
###
### PLEASE CHANGE THIS
###
- DEFAULT_TOKEN_REFRESH_DAYS=7
- DEFAULT_TOKEN_EXPIRE_MIN=10080 # 10080 min --> 7 days
- DEFAULT_DJANGO_DEBUG=true
gpio:
image: c3buttons-gpio:latest
container_name: c3buttons-gpio
restart: unless-stopped
#########
# healthcheck:
# test: ["CMD-SHELL", "curl -f http://localhost:8000/auth/test/open || exit 1"]
# interval: 30s
# timeout: 5s
# retries: 5
#########
# depends_on:
# django:
# condition: service_healthy
links:
- django
privileged: true
devices:
- /dev/gpiomem:/dev/gpiomem
volumes:
# - /dev/gpiomem:/dev/gpiomem
### DEBUG
- ../gpio_buttons:/c3buttons
environment:
###
### PLEASE CHANGE THIS
###
- 'DATABASE_API_TOKEN=<PLEASE_CHANGE_THIS>'
- 'BOXES_USED="BUTTON_BOX_1, BUTTON_BOX_2, BUTTON_BOX_3"'
# - 'BOXES_USED="BUTTON_BOX_1, BUTTON_BOX_2"'
- 'BUTTON_BOX_1={"pin_button": 16, "pin_led": 14}'
- 'BUTTON_BOX_2={"pin_button": 20, "pin_led": 19}'
- 'BUTTON_BOX_3={"pin_button": 21, "pin_led": 13}'
grafana:
image: grafana/grafana:latest
container_name: c3buttons-grafana
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/health || exit 1"]
interval: 30s
timeout: 5s
retries: 5
# depends_on:
# django:
# condition: service_healthy
links:
- django
### DISABLE PORTS FOR PRODUCTION
ports:
- '3000:3000'
volumes:
- ./grafana/data:/var/lib/grafana
environment:
- FOO=BAR
- GF_FEATURE_TOGGLES_PUBLICDASHBOARDS=true
- TLS_SKIP_VERIFY_INSECURE=true
proxy:
image: 'jc21/nginx-proxy-manager:latest'
container_name: c3buttons-proxy
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:81 || exit 1"]
interval: 30s
timeout: 5s
retries: 5
# depends_on:
# django:
# condition: service_healthy
# grafana:
# condition: service_healthy
links:
- django
- grafana
ports:
- '80:80'
- '443:443'
### This is the admin port, disable for Production
- '81:81'
volumes:
- ./proxy/data:/data
- ./proxy/letsencrypt:/etc/letsencrypt
FROM ubuntu:24.04
# Set variables
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ="Europe/Berlin"
# Install dependencies
RUN apt-get update -qq && apt-get upgrade -qqy \
# python3-dev \
# python3-venv \
&& apt-get install -qqy \
python3-pip \
python3-lgpio \
python3-pigpio \
python3-rpi.gpio \
htop \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# New debian base FUCKING shit
RUN rm -rfv /usr/lib/python3*/EXTERNALLY-MANAGED
# Change user for installing python packages -> Set user and group
ARG user=appuser
ARG group=appuser
ARG uid=2000
ARG gid=2000
RUN groupadd -g ${gid} ${group}
# <--- the '-m' create a user home directory
RUN useradd -u ${uid} -g ${group} -s /bin/sh -m ${user}
# Create the folder at the container
WORKDIR /home/appuser/c3buttons
# Install python requirements
COPY ../../gpio_buttons/requirements.txt ./
RUN pip3 install -r requirements.txt
# Install GPIO
RUN pip3 install gpiozero --break-system-packages
# Copy App
COPY ../../gpio_buttons/ .
### TEST
CMD ["sleep", "1000000"]
### PROD
# CMD ["python3", "main.py"]
DESCRIPTION='<PLEASE_SET_THIS>'
# DEBUG='false'
DEBUG='true'
VERBOSE='false'
###
### DATABASE_TYPE --> 'local_file' = using local running django with SQLite | 'api' = access it with API
###
# DATABASE_TYPE='local_file'
DATABASE_TYPE='api'
###
### DATABASE_TYPE: local_file
###
# Pi
DATABASE_LOCAL_DB_FILE_PATH='/home/pi/Coding/c3buttons/django/db.sqlite3'
# # Local
# DATABASE_LOCAL_DB_FILE_PATH='/home/fejao/Coding/CCC/git_cccv/c3buttons/django/db.sqlite3'
###
### DATABASE_TYPE: api
###
# Pi
DATABASE_API_URL='http://django:8000'
DATABASE_API_TOKEN='<PLEASE_SER_THIS>'
# # Local
# DATABASE_API_URL='http://localhost:8000'
# DATABASE_API_TOKEN='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQxNjMxMTI3LCJpYXQiOjE3NDEwMjYzMjcsImp0aSI6Ijk5MzFjMjhkNGE2MDQ4MTg5ZGYwYTEzZDcyNzE2NDI5IiwidXNlcl9pZCI6MX0.fNIm-6bai4jJkL5NMSW1K9mWIP_XmHzPEkTxgzqJd_4'
###
### BOXES CONFIG
###
BOX_LED_BLINK_TIMES=3
# BOX_LED_ENABLED='false'
BOX_LED_ENABLED='true'
# BOX_LED_AWAYS_ON='true'
BOX_LED_AWAYS_ON='false'
### BOX_BUTTON_BOUNCE_TIME in microseconds
BOX_BUTTON_BOUNCE_TIME= '0.1'
###
### BOXES USED
###
BUTTON_BOX_1='{"pin_button": 16, "pin_led": 14}'
BUTTON_BOX_2='{"pin_button": 20, "pin_led": 19}'
# This is the list of the boxes used, if you add new ones, please alse add it's configuration
BOXES_USED='BUTTON_BOX_1, BUTTON_BOX_2'
#!/usr/bin/env python3
import sys
###
from signal import pause
try:
from utils.configuration.app_configuration import AppConfiguration
except ImportError as e:
print("Unable to import 'AppConfiguration' from 'utils.configuration.app_configuration.py' file at the 'main.py'")
sys.exit(1)
try:
from utils.database.app_database import AppDatabase
except ImportError as e:
print("Unable to import 'AppDatabase' from 'utils.database.app_database.py' file at the 'main.py'")
sys.exit(1)
try:
from utils.boxes.app_boxes import AppBoxes
except ImportError as e:
print("Unable to import 'AppDatabase' from 'utils.database.app_database.py' file at the 'main.py'")
sys.exit(1)
#############
#############
#############
BTN_COUNTER = 0
def btn_pressed():
global BTN_COUNTER
print(f"button pressed | counter: {BTN_COUNTER}")
BTN_COUNTER += 1
def btn_pressed_2():
global BTN_COUNTER
print(f"button pressed_2 | counter: {BTN_COUNTER}")
BTN_COUNTER += 1
# main function
def main():
print("\n-------> START \n")
# Get the configuration, from file or parsed...
configuration = AppConfiguration()
###
### DEBUG
###
from pprint import pp
# import pdb; pdb.set_trace()
# pp(configuration.dict_values)
# Set the used DB from the configuration...
database = AppDatabase(configuration)
###
### LOCALSQLite
###
# ### WORKS
# foo = database.db.table_select_all(configuration.database_table_name_button_box)
# import pdb; pdb.set_trace()
# pp(foo)
###
### API
###
# # import pdb; pdb.set_trace()
# test_data = {
# # "id": "1"
# "id": "2"
# }
# foo = database.db.post(endpoint="api/box/use", json=test_data)
# pp(foo)
# bar = database.db.get(endpoint="api/box/use/count")
# pp(bar)
# Set the Button Boxes
boxes = AppBoxes(configuration, database)
import pdb; pdb.set_trace()
pp(boxes)
print("\n <------- FINISH \n")
if __name__=="__main__":
main()
# argparse
###
# logging
pyaml-env
dotenv
requests
# gpiozero
#!/usr/bin/env python3
"""
utils.boxes.app_boxes.py file for the c3buttons system.
"""
###
### DEPENDENCIES
###
import sys
from signal import pause
### LOCAL
try:
from utils.configuration.app_logger import set_logger_from_config
except ImportError as e:
print("Unable to import 'setup_logger' from 'utils.configuration.app_logger.py' file at 'utils.database.app_database.py' file")
sys.exit(1)
try:
from .box_led import BoxLed
except ImportError as e:
print("Unable to import 'BoxLed' from 'utils.boxes.box_led.py' file at 'utils.boxes.app_boxes.py' file" )
sys.exit(1)
try:
from .box_button import BoxButton
except ImportError as e:
print("Unable to import 'BoxButton' from 'utils.boxes.box_button.py' file at 'utils.boxes.app_boxes.py' file" )
sys.exit(1)
class AppBoxes():
"""
Class for setting the Boxes of the app.
"""
def __init__(self, configuration: object, database: object) -> None:
"""
Function for initialiazing the AppConfiguration class.
Sets the class variables.
Returns
-------
None
"""
self.configuration = configuration
self.database = database
self.logger = set_logger_from_config(name=__name__, config=self.configuration)
self.buttons = {}
self.set_boxes()
self.set_button_functions()
def set_boxes(self):
self.logger.debug("set_boxes")
for box_name in self.configuration.boxes_used:
if self.configuration.boxes.get(box_name) is not None:
box_info = self.configuration.boxes.get(box_name)
if box_info.get('pin_button') is None:
print(f"\n\n The 'pin_button' value for box with name '{box_name}' was not set" )
sys.exit(1)
if box_info.get('pin_led') is None:
print(f"\n\n The 'pin_led' value for box with name '{box_name}' was not set" )
sys.exit(1)
box_led = None
if self.configuration.box_led_enabled:
box_led = BoxLed(
configuration=self.configuration,
box_name=box_name,
)
box_button = BoxButton(
configuration=self.configuration,
box_name=box_name,
database=self.database,
box_led=box_led,
)
self.buttons[box_name] = box_button
def set_button_functions(self):
self.logger.debug("set_button_functions")
try:
for btn_name, btn in self.buttons.items():
btn.button.when_pressed = btn.activated
self.logger.debug(f"Button functions for box: '{btn_name}' activated...")
pause()
finally:
pass
#!/usr/bin/env python3
"""
utils.database.from_api.py file for the c3 buttons.
"""
###
### DEPENDENCIES
###
import sys
from gpiozero import Button
from datetime import datetime
### LOCAL
try:
from .box_pin import BoxPin
except ImportError as e:
print("Unable to import 'BoxPin' from 'utils.boxes.box_pin.py' file at 'utils.boxes.box_button.py' file")
sys.exit(1)
class BoxButton(BoxPin):
"""
TODO: Documentation
"""
def __init__(self, configuration: dict, box_name: str, database: object, box_led: str = None) -> None:
"""
TODO: Documentation
"""
### Init super
super().__init__(name=__name__, configuration=configuration, box_name=box_name)
self.logger.debug(f"__init__ - box_name: '{box_name}'")
self.box_led = box_led
self.database = database
self.set_pin_usage()
def set_pin_usage(self):
self.logger.debug(f"set_pin_usage - box_name: '{self.box_name}'")
self.button_press_count = 0
try:
self.bounce_time = float(self.configuration.box_button_bounce_time)
except Exception as e:
print(f"\n\n Error setting the bounce_time from button with exception: {e}")
sys.exit(1)
self.button = Button(self.pin_button, bounce_time=self.bounce_time)
def update_db_from_activated(self):
self.logger.debug(f"update_db_from_activated - box_name '{self.box_name}' | pin_button: '{self.pin_button}'")
self.database.add_button_box_usage(box_name=self.box_name)
def activated(self):
self.logger.debug(f"activated - box_name '{self.box_name}' | pin_button: '{self.pin_button}' | count: '{self.button_press_count}'")
self.button_press_count += 1
### Update DB
self.database.db.add_button_box_usage(box_name=self.box_name)
### Set LED activation
if self.box_led:
self.box_led.activated()
#!/usr/bin/env python3
"""
utils.database.from_api.py file for the c3 buttons.
"""
###
### DEPENDENCIES
###
import sys
import time
from gpiozero import LED
### LOCAL
try:
from .box_pin import BoxPin
except ImportError as e:
print("Unable to import 'BoxPin' from 'utils.boxes.box_pin.py' file at 'utils.boxes.box_button.py' file" )
sys.exit(1)
class BoxLed(BoxPin):
"""
TODO: Documentation
"""
def __init__(self, configuration: dict, box_name: str) -> None:
"""
TODO: Documentation
"""
### Init super
super().__init__(name=__name__, configuration=configuration, box_name=box_name)
self.logger.debug(f"__init__ - box_name: '{box_name}'")
self.blink_times = int(self.configuration.box_led_blink_times)
self.set_pin_usage()
def set_pin_usage(self):
self.logger.debug(f"set_pin_usage - box_name: '{self.box_name}'")
self.led = LED(self.pin_led)
if self.configuration.box_led_always_on:
self.led.on()
def activated(self):
self.logger.debug(f"activated - box_name: '{self.box_name}' | pin_led: '{self.pin_led}'")
i = 0
if self.configuration.box_led_always_on:
while i in range(self.blink_times):
self.led.off()
time.sleep(0.5)
self.led.on()
time.sleep(0.5)
i += 1
self.led.on()
else:
while i in range(self.blink_times):
self.led.on()
time.sleep(0.5)
self.led.off()
time.sleep(0.5)
i += 1
self.led.off()