First version

This commit is contained in:
Andrea Mistrali 2023-05-01 07:04:56 +02:00
parent e353306129
commit 20c5d9c4ba
No known key found for this signature in database
GPG Key ID: 6FB0A77F6D5DA9B2
8 changed files with 361 additions and 3 deletions

8
.gitignore vendored
View File

@ -5,7 +5,8 @@
.LSOverride
# Icon must end with two \r
Icon
Icon
# Thumbnails
._*
@ -224,8 +225,8 @@ tags
crash.log
# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
*.tfvars
@ -248,3 +249,4 @@ override.tf.json
.terraformrc
terraform.rc
.python-version

9
build.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
if [ "$1" = "clean" ]; then
rm -rf build
rm -rf dist
rm -rf *.egg-info
fi
python -m build .

0
pulses/__init__.py Normal file
View File

318
pulses/pulses.py Normal file
View File

@ -0,0 +1,318 @@
import logging
import sys
import math
import time
import threading
if sys.platform == "linux":
import RPi.GPIO as GPIO
class ledWorker(threading.Thread):
"""
This is the thread that runs the main loop
and actually drives the LED
"""
def __init__(self, parent):
super().__init__(name=parent.name, daemon=True)
self.log = logging.getLogger(self.__class__.__name__)
self.parent = parent
self.changed = self.parent.changed
self.stop_event = threading.Event()
def run(self):
self.log.debug(f"running {self.name} main loop...")
self.parent.loop()
def stop(self):
self.stop_event.set()
self.log.debug(f"stopping {self.name}...")
class ledPulse:
"""
There are 4 states of pulsing:
- on: steady light at value;
- off: turn off led;
- normal: pulse with delay;
- fast: pulse with delay/2;
"""
delayModes = ['constant', 'sin', 'cos']
valueModes = ['on', 'off', 'linear', 'sin', 'cos']
modes = {'delay': {}, 'value': {}}
defaultSet = {
'min': 2,
'max': 100,
'delay': 0.01,
'delayMode': 'constant',
'initialMode': None,
'valueMode': 'linear',
'finalMode': None
}
def __init__(self, gpio, name="led0"):
self.log = logging.getLogger(self.__class__.__name__)
self.gpio = gpio
self.name = name
self.changed = threading.Event()
self.initialStep = 0
self._set = False
if sys.platform == "linux":
GPIO.setwarnings(False) # disable warnings
GPIO.setmode(GPIO.BCM) # set pin numbering system
# set GPIO for output
GPIO.setup(self.gpio, GPIO.OUT)
# create PWM instance with frequency
self.pwm = GPIO.PWM(self.gpio, 120)
# self.setValue = self.pwm.ChangeDutyCycle
self.supported = True
# self.pwm.start(0) # start pwm with value 0
else:
# Not supported, skip setValue
# self.setValue = print
self.supported = False
# self.setValue = lambda x: x
self.log.info(f"platform '{sys.platform}' " +
f"support: {sys.platform == 'linux'}")
for dm in self.delayModes:
self.register_delay_method(dm, eval(f"delay_{dm}"))
for vm in self.valueModes:
self.register_value_method(vm, eval(f"value_{vm}"))
# Add thread placeholder
self.worker = None
# Set default values
for key, value in self.defaultSet.items():
setattr(self, key, value)
def setValue(self, value):
if self.supported:
self.pwm.ChangeDutyCycle(value)
else:
# on unsupported platforms, we just print the value
print(value)
# def set(self, min=2, max=50, delay=1,
# delayMode='constant',
# valueMode='linear',
# initialMode=None,
# finalMode=None):
def set(self, **kwargs):
# for key, value in self.defaultSet.items():
# setattr(self, key, value)
for key, value in kwargs.items():
if key in self.defaultSet.keys():
setattr(self, key, value)
self._set = True
else:
self.log.warning(f"set: unknown parameter '{key}'")
self.delta = self.max - self.min
# self.min = min
# self.max = max
# self.delay = delay
# self.delayMode = delayMode
# self.valueMode = valueMode
# self.initialMode = initialMode
# self.finalMode = finalMode
if self._set:
if self.worker and self.worker.is_alive():
print("worker running, notify change")
self.worker.changed.set()
# VALUES CALCULATION
def calcValues(self, initialStep, finalStep):
if not (self.delayMode in self.modes['delay']
and self.valueMode in self.modes['value']):
self.log.error(f"Invalid parameters, delayMode:{self.delayMode}, "
f"valueMode:{self.valueMode}")
return
self.log.debug(f"fill cycle values table, valueMode:{self.valueMode}, "
f"delayMode:{self.delayMode}, min:{self.min}, "
f"max:{self.max}, delay:{self.delay}")
values = []
for step in range(initialStep, finalStep):
try:
delay = self.modes['delay'][self.delayMode](self, step)
except NameError:
self.log.error(f"delay function for '{self.delayMode}' "
"not found")
return
try:
value = self.modes['value'][self.valueMode](self, step)
except NameError:
self.log.error(f"value function for '{self.valueMode}' not found")
return
values.append((value, delay))
return values
def cycleValues(self):
return self.calcValues(0, 100)
def initialValues(self):
return self.calcValues(0, 50)
def finalValues(self):
return self.calcValues(50, 100)
def start(self):
if self.supported:
self.pwm.start(0)
self.worker = ledWorker(self)
self.worker.start()
def stop(self):
self.worker.stop()
self.worker.join()
if self.supported:
self.pwm.stop()
self.worker = None
def loop(self):
if not self._set:
self.log.error("led modes not set, use .set() first")
return
while True:
# Initial
if self.initialMode:
self.log.debug('running initial step')
vals = self.initialValues()
for value, delay in vals:
self.setValue(value)
time.sleep(delay)
print('end initial step')
self.log.debug('end initial step')
vals = self.cycleValues()
while True:
if self.worker.stop_event.is_set():
# self.worker._started.clear()
self.worker.stop_event.clear()
self.log.debug("bailing out from infinite loop")
return
if self.worker.changed.is_set():
self.changed.clear()
self.log.debug("parameters changed, recalculate values...")
break
for step in range(len(vals)):
value, delay = vals[step]
self.setValue(value)
time.sleep(delay)
# Final
if self.finalMode:
print('running final step')
self.log.debug('running final step')
vals = self.finalValues()
for value, delay in vals:
self.setValue(value)
time.sleep(delay)
print('end final step')
self.log.debug('end final step')
###
# PLUGINS METHODS
###
def register_value_method(self, name, fn):
self.register_method('value', name, fn)
def register_delay_method(self, name, fn):
self.register_method('delay', name, fn)
def register_method(self, kind, name, fn):
if kind not in ['delay', 'value']:
self.log.warning(f"unknown kind {kind}")
return
if name not in self.modes[kind]:
self.modes[kind][name] = fn
self.log.info(f"registered {kind} method '{name}'")
else:
self.log.warning(f"{kind} method '{name}' already defined")
####
# Predefined functions
###
# Delays
###
def delay_sin(obj, step):
return math.sin(math.radians(1.8*step)) * obj.delay
def delay_cos(obj, step):
return abs(math.cos(math.radians(1.8*step))) * obj.delay
def delay_constant(obj, step):
return obj.delay
###
# Values
###
def value_on(obj, step):
"""
Always on at "max" brightness
"""
return obj.max
def value_off(obj, step):
"""
Always off
"""
return 0
def value_linear(obj, step):
"""
Saw patterned value, 0-1-0
"""
if step < 50:
return obj.delta*step/50 + obj.min
else:
return obj.delta * (100-step)/50 + obj.min
def value_sin(obj, step):
"""
Sinusoidal values, 0-1-0 /\
"""
radians = math.radians(1.8 * step)
return obj.delta * math.sin(radians) + obj.min
def value_cos(obj, step):
"""
Absolute Cosinusoidal values, 1-0-1 \\//
"""
radians = math.radians(1.8 * step)
return obj.delta * abs(math.cos(radians)) + obj.min

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

2
requirements-dev.txt Normal file
View File

@ -0,0 +1,2 @@
build
ipython

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
build
PyYAML
rpi-backlight

21
setup.cfg Normal file
View File

@ -0,0 +1,21 @@
[metadata]
name = mblight
version = 0.90
author = Andrea Mistrali
author_email = akelge@gmail.com
platform = linux
description = RPI led(s) and display brightness
long_description = file: README.md
keywords = graylog, py3
license = BSD 3-Clause License
[options]
packages = mblight
python_requires = >=3
include_package_data = True
install_requires =
PyYAML
[options.package_data]
* =
config.yaml.example