From 6ee8913fb793a1bc9b8e3a1fff19943c5c4a1671 Mon Sep 17 00:00:00 2001 From: Bruce Schubert Date: Sun, 30 Aug 2020 07:17:23 -0700 Subject: [PATCH 1/3] Updated configuration of LEDs to include GPIO pin and brightness. - Re: #88 --- callattendant/app.cfg.example | 24 +++-- callattendant/app.py | 8 +- callattendant/config.py | 12 ++- callattendant/hardware/indicators.py | 128 ++++++++++++++++----------- callattendant/hardware/modem.py | 5 +- callattendant/messaging/voicemail.py | 8 +- tests/test_indicators.py | 30 +++---- 7 files changed, 133 insertions(+), 82 deletions(-) diff --git a/callattendant/app.cfg.example b/callattendant/app.cfg.example index 08d907b..0456892 100644 --- a/callattendant/app.cfg.example +++ b/callattendant/app.cfg.example @@ -128,9 +128,23 @@ VOICE_MAIL_MENU_FILE = "resources/voice_mail_menu.wav" # This should not be changed/overridden except during development/testing #VOICE_MAIL_MESSAGE_FOLDER = "messages" -GPIO_LED_RING = 4 -GPIO_LED_APPROVED = 15 -GPIO_LED_BLOCKED = 17 -GPIO_LED_MESSAGE = 14 +# GPIO_LED_..._PIN: These values are the GPIO pin numbers attached to the LED indicators +# GPIO_LED_..._BRIGHTNESS: These values are a percentage of brightness for the LED indicators when on. +GPIO_LED_RING_PIN = 4 +GPIO_LED_RING_BRIGHTNESS = 100 + +GPIO_LED_APPROVED_PIN = 15 +GPIO_LED_APPROVED_BRIGHTNESS = 100 + +GPIO_LED_BLOCKED_PIN = 17 +GPIO_LED_BLOCKED_BRIGHTNESS = 100 + +GPIO_LED_MESSAGE_PIN = 14 +GPIO_LED_MESSAGE_BRIGHTNESS = 100 + +# GPIO_LED_MESSAGE_COUNT_PINS: This is a tuple containing the GPIO pins for the 7-segment LED +# ordered by segment as thus: (a, b, c, d, e, f, g, dp) GPIO_LED_MESSAGE_COUNT_PINS = (11, 8, 25, 5, 18, 9, 7, 27) -GPIO_LED_MESSAGE_COUNT_KWARGS = {active_high="False"} + +# GPIO_LED_MESSAGE_COUNT_KWARGS: This is a dictionary of optional arguments (used by GPIOZero LEDBoard) +GPIO_LED_MESSAGE_COUNT_KWARGS = {"active_high": False} diff --git a/callattendant/app.py b/callattendant/app.py index 6d1e37e..2df7d5e 100755 --- a/callattendant/app.py +++ b/callattendant/app.py @@ -60,8 +60,12 @@ def __init__(self, config): self._caller_queue = queue.Queue() # Initialize the visual indicators (LEDs) - self.approved_indicator = ApprovedIndicator() - self.blocked_indicator = BlockedIndicator() + self.approved_indicator = ApprovedIndicator( + self.config.get("GPIO_LED_APPROVED_PIN"), + self.config.get("GPIO_LED_APPROVED_BRIGHTNESS", 100)) + self.blocked_indicator = BlockedIndicator( + self.config.get("GPIO_LED_BLOCKED_PIN"), + self.config.get("GPIO_LED_BLOCKED_BRIGHTNESS", 100)) # Screening subsystem self.logger = CallLogger(self.db, self.config) diff --git a/callattendant/config.py b/callattendant/config.py index 871b454..ffc6c6c 100644 --- a/callattendant/config.py +++ b/callattendant/config.py @@ -48,10 +48,14 @@ "VOICE_MAIL_MENU_FILE": "resources/voice_mail_menu.wav", "VOICE_MAIL_MESSAGE_FOLDER": "messages", - "GPIO_LED_RING": 14, - "GPIO_LED_APPROVED": 15, - "GPIO_LED_BLOCKED":17, - "GPIO_LED_MESSAGE": 4, + "GPIO_LED_RING_PIN": 14, + "GPIO_LED_RING_BRIGHTNESS": 100, + "GPIO_LED_APPROVED_PIN": 15, + "GPIO_LED_APPROVED_BRIGHTNESS": 100, + "GPIO_LED_BLOCKED_PIN":17, + "GPIO_LED_BLOCKED_BRIGHTNESS":100, + "GPIO_LED_MESSAGE_PIN": 4, + "GPIO_LED_MESSAGE_BRIGHTNESS": 100, "GPIO_LED_MESSAGE_COUNT_PINS": (11, 8, 25, 5, 18, 9, 7, 27), "GPIO_LED_MESSAGE_COUNT_KWARGS": {"active_high": False}, } diff --git a/callattendant/hardware/indicators.py b/callattendant/hardware/indicators.py index d10f3b0..61c3fb9 100644 --- a/callattendant/hardware/indicators.py +++ b/callattendant/hardware/indicators.py @@ -38,47 +38,6 @@ GPIO_MESSAGE_COUNT_KWARGS = {"active_high": False} -class LEDIndicator(object): - def turn_on(self): - self.led.on() - - def blink(self, max_times=10): - # blink in a separate thread - self.led.blink(0.5, 0.2, max_times) - - def turn_off(self): - self.led.off() - - def close(self): - self.led.close() - - def __init__(self, gpio_pin): - self.led = LED(gpio_pin) - - -class PWMLEDIndicator(object): - - def __init__(self, gpio_pin): - self.led = PWMLED(gpio_pin) - - def turn_on(self): - self.led.on() - - def turn_off(self): - self.led.off() - - def blink(self, max_times=10): - # blink in a separate thread - self.led.blink(0.5, 0.2, n=max_times) - - def pulse(self, max_times=10): - # pulse in a separate thread - self.led.pulse(n=max_times) - - def close(self): - self.led.close() - - class SevenSegmentDisplay(LEDBoard): """ Extends :class:`LEDBoard` for a 7 segment LED display @@ -243,10 +202,66 @@ def set_char_layout(self, char, layout): self._layouts[char] = layout -class RingIndicator(PWMLEDIndicator): +class LEDIndicator(object): + def turn_on(self): + self.led.on() - def __init__(self, gpio_pin=GPIO_RING): - super().__init__(gpio_pin) + def blink(self, max_times=10): + # blink in a separate thread + self.led.blink(0.5, 0.2, max_times) + + def turn_off(self): + self.led.off() + + def close(self): + self.led.close() + + def __init__(self, gpio_pin): + self.led = LED(gpio_pin) + + +class PWMLEDIndicator(object): + """ + A pulse-width modulated LED. + """ + + def __init__(self, gpio_pin, brightness=100): + """ + Constructor of a PWM LED. + :param gpio_pin: + GPIO pin assignment (not the header pin number) + :param brightness: + Brightness percentage. Defaults to 100%. + """ + self.led = PWMLED(gpio_pin) + self.brightness = brightness / 100.0 # brightness value is from 0 to 1.0 + + def turn_on(self): + # ~ self.led.on() + self.led.value = self.brightness + + def turn_off(self): + # ~ self.led.off() + self.led.value = 0 + + def blink(self, max_times=10): + # blink in a separate thread + self.led.blink(0.5, 0.2, n=max_times) + + def pulse(self, max_times=10): + # pulse in a separate thread + self.led.pulse(n=max_times) + + def close(self): + self.led.close() + + +class RingIndicator(PWMLEDIndicator): + """ + The ring indicator, activated when an incoming call is being received. + """ + def __init__(self, gpio_pin=GPIO_RING, brightness=100): + super().__init__(gpio_pin, brightness) def ring(self): self.blink() @@ -254,21 +269,29 @@ def ring(self): class ApprovedIndicator(PWMLEDIndicator): - - def __init__(self, gpio_pin=GPIO_APPROVED): - super().__init__(gpio_pin) + """ + The approved indicator activated when a call from a permitted number is received. + """ + def __init__(self, gpio_pin=GPIO_APPROVED, brightness=100): + super().__init__(gpio_pin, brightness) class BlockedIndicator(PWMLEDIndicator): + """ + The blocked indicator activated when a call from a blocked number is received. + """ - def __init__(self, gpio_pin=GPIO_BLOCKED): - super().__init__(gpio_pin) + def __init__(self, gpio_pin=GPIO_BLOCKED, brightness=100): + super().__init__(gpio_pin, brightness) class MessageIndicator(PWMLEDIndicator): + """ + The message indicator activated when the voice messaging features are used. + """ - def __init__(self, gpio_pin=GPIO_MESSAGE): - super().__init__(gpio_pin) + def __init__(self, gpio_pin=GPIO_MESSAGE, brightness=100): + super().__init__(gpio_pin, brightness) def turn_off(self): print("{MSG LED OFF}") @@ -288,6 +311,9 @@ def pulse(self): class MessageCountIndicator(SevenSegmentDisplay): + """ + The message count indicator displays the number of unplayed messages in the system. + """ def __init__(self, *pins, **kwargs): if len(pins) > 0: diff --git a/callattendant/hardware/modem.py b/callattendant/hardware/modem.py index dfdd25a..2455f6a 100644 --- a/callattendant/hardware/modem.py +++ b/callattendant/hardware/modem.py @@ -123,7 +123,9 @@ def __init__(self, config, handle_caller): self._lock = threading.RLock() # Ring notifications - self.ring_indicator = RingIndicator() + self.ring_indicator = RingIndicator( + self.config.get("GPIO_LED_RING_PIN"), + self.config.get("GPIO_LED_RING_BRIGHTNESS", 100)) self.ring_event = threading.Event() # Setup and open the serial port @@ -672,4 +674,3 @@ def close_serial_port(self): def decode(bytestr): string = bytestr.decode("utf-8").strip(' \t\n\r' + DLE_CODE) return string - diff --git a/callattendant/messaging/voicemail.py b/callattendant/messaging/voicemail.py index 22b8c7a..1c96020 100644 --- a/callattendant/messaging/voicemail.py +++ b/callattendant/messaging/voicemail.py @@ -49,9 +49,11 @@ def __init__(self, db, config, modem): self.config["MESSAGE_EVENT"] = self.message_event # Initialize the message indicators (LEDs) - self.message_indicator = MessageIndicator(config.get("GPIO_LED_MESSAGE", GPIO_MESSAGE)) - pins = config.get("GPIO_LED_MESSAGE_COUNT_PINS", GPIO_MESSAGE_COUNT_PINS) - kwargs = config.get("GPIO_LED_MESSAGE_COUNT_KWARGS", GPIO_MESSAGE_COUNT_KWARGS) + self.message_indicator = MessageIndicator( + self.config.get("GPIO_LED_MESSAGE_PIN", GPIO_MESSAGE), + self.config.get("GPIO_LED_MESSAGE_BRIGHTNESS", 100)) + pins = self.config.get("GPIO_LED_MESSAGE_COUNT_PINS", GPIO_MESSAGE_COUNT_PINS) + kwargs = self.config.get("GPIO_LED_MESSAGE_COUNT_KWARGS", GPIO_MESSAGE_COUNT_KWARGS) self.message_count_indicator = MessageCountIndicator(*pins, **kwargs) # Create the Message object used to interface with the DB diff --git a/tests/test_indicators.py b/tests/test_indicators.py index 10056d7..1b6ca66 100644 --- a/tests/test_indicators.py +++ b/tests/test_indicators.py @@ -40,24 +40,24 @@ def test_multiple(): config = Config() - # ~ ringer = RingIndicator(config["GPIO_LED_RING"]) - # ~ approved = ApprovedIndicator(config["GPIO_LED_APPROVED"]) - # ~ blocked = BlockedIndicator(config["GPIO_LED_BLOCKED"]) - # ~ message = MessageIndicator(config["GPIO_LED_MESSAGE"]) + ringer = RingIndicator(config["GPIO_LED_RING_PIN"], brightness=100) + approved = ApprovedIndicator(config["GPIO_LED_APPROVED_PIN"], brightness=25) + blocked = BlockedIndicator(config["GPIO_LED_BLOCKED_PIN"], brightness=25) + message = MessageIndicator(config["GPIO_LED_MESSAGE_PIN"], brightness=100) - # ~ pins_tuple = config["GPIO_LED_MESSAGE_COUNT_PINS"] - # ~ kwargs_dict = config["GPIO_LED_MESSAGE_COUNT_KWARGS"] - # ~ message_count = MessageCountIndicator(*pins_tuple, **kwargs_dict) + pins_tuple = config["GPIO_LED_MESSAGE_COUNT_PINS"] + kwargs_dict = config["GPIO_LED_MESSAGE_COUNT_KWARGS"] + message_count = MessageCountIndicator(*pins_tuple, **kwargs_dict) - ringer = RingIndicator() - approved = ApprovedIndicator() - blocked = BlockedIndicator() - message = MessageIndicator() - message_count = MessageCountIndicator() + # ~ ringer = RingIndicator() + # ~ approved = ApprovedIndicator() + # ~ blocked = BlockedIndicator() + # ~ message = MessageIndicator() + # ~ message_count = MessageCountIndicator() for i in range(0, 16): message_count.display_hex(i) - time.sleep(1) + time.sleep(.5) print("[Visual Tests]") @@ -83,14 +83,14 @@ def test_multiple(): approved.turn_off() blocked.turn_off() message.turn_off() - time.sleep(5) + time.sleep(2) print("Test normal status") ringer.ring() message.pulse(), message_count.display(2) - time.sleep(5) + time.sleep(10) # Release GPIO pins ringer.close() From 40950b84c6a51d70071e0f9c2b3d47e95b448b41 Mon Sep 17 00:00:00 2001 From: Bruce Schubert Date: Sun, 30 Aug 2020 07:23:05 -0700 Subject: [PATCH 2/3] Added VERSION to config to hold the semantic version number --- callattendant/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/callattendant/config.py b/callattendant/config.py index ffc6c6c..1d82c1e 100644 --- a/callattendant/config.py +++ b/callattendant/config.py @@ -19,9 +19,12 @@ # and screened callers through to the home phone. # default_config = { + "VERSION": '1.0.0', + "ENV": 'production', "DEBUG": False, "TESTING": False, + "DATABASE": "callattendant.db", "SCREENING_MODE": ("whitelist", "blacklist"), From 1e3e48bd2fe873270262e08d340d419e243f5073 Mon Sep 17 00:00:00 2001 From: Bruce Schubert Date: Sun, 30 Aug 2020 07:23:34 -0700 Subject: [PATCH 3/3] Updated semantic version to 1.0.0 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 6a15472..9bfee7d 100644 --- a/setup.py +++ b/setup.py @@ -13,8 +13,8 @@ long_description = fh.read() setuptools.setup( - name="callattendant", # Add user name for upload to TestPyPI - version="0.5.0", + name="callattendant", # Add user name when uploading to TestPyPI + version="1.0.0", # Ensure this is in-sync with VERSION in config.py author="Bruce Schubert", author_email="bruce@emxsys.com", description="An automated call attendant and call blocker using a Raspberry Pi and USR-5637 modem",