IS-Matrix-Forge 1.0.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- is_matrix_forge/__init__.py +8 -0
- is_matrix_forge/assets/__init__.py +8 -0
- is_matrix_forge/assets/audio/__init__.py +36 -0
- is_matrix_forge/assets/audio/files/__init__.py +53 -0
- is_matrix_forge/assets/audio/files/plugged.wav +0 -0
- is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
- is_matrix_forge/assets/char_map.json +2150 -0
- is_matrix_forge/assets/font_map.py +119 -0
- is_matrix_forge/common/__init__.py +0 -0
- is_matrix_forge/common/decorators/__init__.py +152 -0
- is_matrix_forge/common/dirs.py +20 -0
- is_matrix_forge/common/helpers/__init__.py +132 -0
- is_matrix_forge/common/helpers/github_api.py +67 -0
- is_matrix_forge/designer_gui/__init__.py +8 -0
- is_matrix_forge/designer_gui/main_window/__init__.py +252 -0
- is_matrix_forge/designer_gui/main_window/layout.py +117 -0
- is_matrix_forge/dev_tools/__init__.py +8 -0
- is_matrix_forge/dev_tools/debug.py +82 -0
- is_matrix_forge/dev_tools/presets.py +47 -0
- is_matrix_forge/inputmodule/__init__.py +164 -0
- is_matrix_forge/inputmodule/b1display.py +160 -0
- is_matrix_forge/inputmodule/c1minimal.py +35 -0
- is_matrix_forge/inputmodule/font.py +2166 -0
- is_matrix_forge/inputmodule/helpers/__init__.py +16 -0
- is_matrix_forge/inputmodule/ledmatrix.py +516 -0
- is_matrix_forge/led_matrix/Scripts/__init__.py +4 -0
- is_matrix_forge/led_matrix/Scripts/battery_monitor.py +152 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/__init__.py +2 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/arguments.py +84 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/main.py +192 -0
- is_matrix_forge/led_matrix/__init__.py +7 -0
- is_matrix_forge/led_matrix/commands/__init__.py +84 -0
- is_matrix_forge/led_matrix/commands/map.py +46 -0
- is_matrix_forge/led_matrix/constants.py +82 -0
- is_matrix_forge/led_matrix/controller/__init__.py +26 -0
- is_matrix_forge/led_matrix/controller/controller.py +635 -0
- is_matrix_forge/led_matrix/controller/helpers/__init__.py +7 -0
- is_matrix_forge/led_matrix/controller/helpers/threading.py +41 -0
- is_matrix_forge/led_matrix/controller/multiton.py +32 -0
- is_matrix_forge/led_matrix/controller/threaded.py +72 -0
- is_matrix_forge/led_matrix/display/__init__.py +13 -0
- is_matrix_forge/led_matrix/display/animations/__init__.py +29 -0
- is_matrix_forge/led_matrix/display/animations/animation.py +593 -0
- is_matrix_forge/led_matrix/display/animations/effects/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/animations/errors.py +23 -0
- is_matrix_forge/led_matrix/display/animations/font/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/animations/font/font.py +4 -0
- is_matrix_forge/led_matrix/display/animations/font/map_builder.py +123 -0
- is_matrix_forge/led_matrix/display/animations/frame/__init__.py +47 -0
- is_matrix_forge/led_matrix/display/animations/frame/base.py +245 -0
- is_matrix_forge/led_matrix/display/animations/frame/helpers.py +67 -0
- is_matrix_forge/led_matrix/display/animations/keep_awake.py +1 -0
- is_matrix_forge/led_matrix/display/animations/plugged.py +0 -0
- is_matrix_forge/led_matrix/display/animations/text_scroller.py +104 -0
- is_matrix_forge/led_matrix/display/effects/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/effects/breather.py +206 -0
- is_matrix_forge/led_matrix/display/effects/fade.py +0 -0
- is_matrix_forge/led_matrix/display/effects/scroller/__init__.py +92 -0
- is_matrix_forge/led_matrix/display/effects/scroller/base.py +76 -0
- is_matrix_forge/led_matrix/display/effects/scroller/helpers.py +62 -0
- is_matrix_forge/led_matrix/display/grid/__init__.py +9 -0
- is_matrix_forge/led_matrix/display/grid/grid.py +208 -0
- is_matrix_forge/led_matrix/display/grid/helpers.py +130 -0
- is_matrix_forge/led_matrix/display/grid/presets/__init__.py +64 -0
- is_matrix_forge/led_matrix/display/grid/presets/manifest.py +172 -0
- is_matrix_forge/led_matrix/display/helpers/__init__.py +110 -0
- is_matrix_forge/led_matrix/display/helpers/columns.py +33 -0
- is_matrix_forge/led_matrix/display/media.py +199 -0
- is_matrix_forge/led_matrix/display/patterns/__init__.py +59 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/__init__.py +91 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/commands.py +3 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +29 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/stencils.py +70 -0
- is_matrix_forge/led_matrix/display/patterns/utils.py +2 -0
- is_matrix_forge/led_matrix/display/text/__init__.py +66 -0
- is_matrix_forge/led_matrix/display/text/scroller.py +136 -0
- is_matrix_forge/led_matrix/errors/__init__.py +4 -0
- is_matrix_forge/led_matrix/errors/base.py +14 -0
- is_matrix_forge/led_matrix/errors/grid.py +37 -0
- is_matrix_forge/led_matrix/errors/matrix.py +68 -0
- is_matrix_forge/led_matrix/errors/misc.py +36 -0
- is_matrix_forge/led_matrix/fonts.py +20 -0
- is_matrix_forge/led_matrix/hardware.py +172 -0
- is_matrix_forge/led_matrix/helpers/__init__.py +165 -0
- is_matrix_forge/led_matrix/helpers/core/__init__.py +17 -0
- is_matrix_forge/led_matrix/helpers/core/lazy_mod_load.py +84 -0
- is_matrix_forge/led_matrix/helpers/device.py +118 -0
- is_matrix_forge/led_matrix/helpers/status_handler.py +14 -0
- is_matrix_forge/led_matrix/helpers/stream/__init__.py +70 -0
- is_matrix_forge/led_matrix/helpers/stream/errors.py +17 -0
- is_matrix_forge/led_matrix/led_matrix.py +25 -0
- is_matrix_forge/led_matrix/status.py +8 -0
- is_matrix_forge/led_matrix/tools/__init__.py +8 -0
- is_matrix_forge/led_matrix/tools/identify.py +27 -0
- is_matrix_forge/led_matrix/utils/__init__.py +41 -0
- is_matrix_forge/log_engine.py +25 -0
- is_matrix_forge/monitor/__init__.py +68 -0
- is_matrix_forge/monitor/errors.py +35 -0
- is_matrix_forge/monitor/events.py +78 -0
- is_matrix_forge/monitor/gui/__init__.py +0 -0
- is_matrix_forge/monitor/gui/errors.py +35 -0
- is_matrix_forge/monitor/gui/event/__init__.py +8 -0
- is_matrix_forge/monitor/gui/event/collection.py +153 -0
- is_matrix_forge/monitor/gui/event/errors.py +58 -0
- is_matrix_forge/monitor/gui/event/event.py +226 -0
- is_matrix_forge/monitor/gui/layout/__init__.py +8 -0
- is_matrix_forge/monitor/gui/layout/base.py +147 -0
- is_matrix_forge/monitor/gui/metaclasses/__init__.py +20 -0
- is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
- is_matrix_forge/monitor/gui/windows/base.py +325 -0
- is_matrix_forge/monitor/gui/windows/main/__init__.py +77 -0
- is_matrix_forge/monitor/gui/windows/main/layout.py +21 -0
- is_matrix_forge/monitor/helpers.py +55 -0
- is_matrix_forge/monitor/monitor.py +431 -0
- is_matrix_forge/notify/__init__.py +0 -0
- is_matrix_forge/notify/sounds/__init__.py +6 -0
- is_matrix_forge/notify/sounds/base.py +106 -0
- is_matrix_forge/progress.py +99 -0
- is_matrix_forge-1.0.0.dev1.dist-info/LICENSE.md +19 -0
- is_matrix_forge-1.0.0.dev1.dist-info/METADATA +190 -0
- is_matrix_forge-1.0.0.dev1.dist-info/RECORD +123 -0
- is_matrix_forge-1.0.0.dev1.dist-info/WHEEL +4 -0
- is_matrix_forge-1.0.0.dev1.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Device helper functions for LED matrix hardware.
|
|
3
|
+
|
|
4
|
+
This module provides utility functions for working with LED matrix devices,
|
|
5
|
+
including functions to convert between different device location formats.
|
|
6
|
+
"""
|
|
7
|
+
from serial import Serial, SerialException
|
|
8
|
+
from serial.tools import list_ports
|
|
9
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
MOD_LOGGER = ROOT_LOGGER.get_child('led_matrix.helpers.device')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def serial_loc_to_physical(dev):
|
|
16
|
+
"""
|
|
17
|
+
Convert the serial location of a device to its physical location.
|
|
18
|
+
|
|
19
|
+
Parameters:
|
|
20
|
+
dev (ListPortInfo):
|
|
21
|
+
The device to convert.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str:
|
|
25
|
+
The physical location of the device.
|
|
26
|
+
"""
|
|
27
|
+
return dev.device
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def device_available(device):
|
|
31
|
+
"""
|
|
32
|
+
Check if a device is available.
|
|
33
|
+
|
|
34
|
+
Parameters:
|
|
35
|
+
device:
|
|
36
|
+
The device to check.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
bool:
|
|
40
|
+
True if the device is available, False otherwise.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
log = MOD_LOGGER.get_child('device_available')
|
|
44
|
+
log.debug(f'Checking if device is available: {device}...')
|
|
45
|
+
|
|
46
|
+
if not check_device(device):
|
|
47
|
+
log.warning(f'Device {device} failed check, thus not available.')
|
|
48
|
+
return False
|
|
49
|
+
else:
|
|
50
|
+
log.debug(f'Device {device} is available.')
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def check_device(device):
|
|
55
|
+
|
|
56
|
+
from serial.tools.list_ports_common import ListPortInfo
|
|
57
|
+
|
|
58
|
+
log = MOD_LOGGER.get_child('check_device')
|
|
59
|
+
log.debug(f'Checking device: {device}...')
|
|
60
|
+
|
|
61
|
+
if not isinstance(device, ListPortInfo):
|
|
62
|
+
emsg = f'device must be of type `ListPortInfo`, not {type(device)}'
|
|
63
|
+
log.error(emsg)
|
|
64
|
+
raise TypeError(emsg)
|
|
65
|
+
|
|
66
|
+
# Both the vendor and product ID must match to be supported
|
|
67
|
+
if device.vid != EXPECTED_VID or device.pid != EXPECTED_PID:
|
|
68
|
+
log.warning(
|
|
69
|
+
f'Given device is not supported. (VID: {device.vid}, PID: {device.pid})'
|
|
70
|
+
)
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
return bool(test_connection(device.device))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_devices():
|
|
77
|
+
log = MOD_LOGGER.get_child('get_devices')
|
|
78
|
+
|
|
79
|
+
log.debug('Getting devices...')
|
|
80
|
+
|
|
81
|
+
ports = list_ports.comports()
|
|
82
|
+
|
|
83
|
+
log.debug(f'Found {len(ports)} devices.')
|
|
84
|
+
log.debug('Filtering and returning devices...')
|
|
85
|
+
return [
|
|
86
|
+
port for port in ports if port.vid == 0x32AC and port.pid == 0x20
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_connection(port_name):
|
|
91
|
+
"""
|
|
92
|
+
Test the connection to a given port.
|
|
93
|
+
|
|
94
|
+
Parameters:
|
|
95
|
+
port_name (str):
|
|
96
|
+
The name of the port to test the: connection to.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
log = MOD_LOGGER.get_child('test_connection')
|
|
102
|
+
log.debug(f'Testing connection to {port_name}...')
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
with Serial(port_name, 115200) as s:
|
|
106
|
+
s.write(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
|
107
|
+
s.flush()
|
|
108
|
+
log.debug('Connection test successful.')
|
|
109
|
+
return True
|
|
110
|
+
except SerialException:
|
|
111
|
+
log.debug('Connection test failed.')
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
DEVICES = get_devices()
|
|
116
|
+
|
|
117
|
+
if len(DEVICES) == 0:
|
|
118
|
+
MOD_LOGGER.warning('No devices found. Please connect a device and try again.')
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
from serial import Serial, SerialException
|
|
3
|
+
|
|
4
|
+
from is_matrix_forge.led_matrix.constants import *
|
|
5
|
+
from is_matrix_forge.led_matrix.helpers.status_handler import get_status
|
|
6
|
+
from is_matrix_forge.led_matrix import send_col
|
|
7
|
+
|
|
8
|
+
from .errors import *
|
|
9
|
+
from ...errors.matrix import MatrixConnectionError, MatrixInUseError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def play_stream(dev, capture, mode: str) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Core routine for piping frames to the LED matrix via serial.
|
|
15
|
+
|
|
16
|
+
Parameters:
|
|
17
|
+
dev (`serial.tools.list_ports_common.ListPortInfo`):
|
|
18
|
+
The serial port descriptor returned by `serial.tools.list_ports`.
|
|
19
|
+
|
|
20
|
+
capture (`cv2.VideoCapture`):
|
|
21
|
+
An already-opened OpenCV capture object.
|
|
22
|
+
|
|
23
|
+
mode ({'camera', 'video'}):
|
|
24
|
+
Identifies the status flag and grayscale conversion table entry.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
None
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
RuntimeError:
|
|
31
|
+
If the serial port is not open.
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
with Serial(dev.device, DEFAULT_BAUDRATE) as s:
|
|
35
|
+
|
|
36
|
+
ok, first_frame = capture.read()
|
|
37
|
+
|
|
38
|
+
if not ok:
|
|
39
|
+
raise StreamSourceUnreadableError()
|
|
40
|
+
except SerialException as e:
|
|
41
|
+
raise MatrixInUseError from e
|
|
42
|
+
|
|
43
|
+
# ── Calculate scaling and crop once based on the first frame ───────
|
|
44
|
+
scale_y = HEIGHT / first_frame.shape[0]
|
|
45
|
+
scaled_w = int(round(first_frame.shape[1] * scale_y))
|
|
46
|
+
start_x = max(0, int(round(scaled_w / 2 - WIDTH / 2)))
|
|
47
|
+
end_x = min(scaled_w, start_x + WIDTH)
|
|
48
|
+
dim = (HEIGHT, scaled_w)
|
|
49
|
+
|
|
50
|
+
# ── Main pump loop ────────────────────────────────────────────────
|
|
51
|
+
while get_status() == mode:
|
|
52
|
+
ok, frame = capture.read()
|
|
53
|
+
if not ok:
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
gray = cv2.cvtColor(frame, __GRAYSCALE_CVT[mode])
|
|
57
|
+
resized = cv2.resize(gray, dim)
|
|
58
|
+
cropped = resized[0:HEIGHT, start_x:end_x]
|
|
59
|
+
|
|
60
|
+
# Stream one column at a time
|
|
61
|
+
for x in range(cropped.shape[1]):
|
|
62
|
+
send_col(
|
|
63
|
+
dev,
|
|
64
|
+
s,
|
|
65
|
+
x,
|
|
66
|
+
[cropped[y, x] for y in range(HEIGHT)]
|
|
67
|
+
)
|
|
68
|
+
commit_cols(dev, s)
|
|
69
|
+
|
|
70
|
+
capture.release()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from inspyre_toolbox.exceptional import CustomRootException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MatrixStreamError(CustomRootException):
|
|
5
|
+
default_message= 'An error occurred while streaming to the LED matrix!'
|
|
6
|
+
|
|
7
|
+
def __init__(self, message=None):
|
|
8
|
+
if message is None:
|
|
9
|
+
message = self.default_message
|
|
10
|
+
else:
|
|
11
|
+
message = f'{self.default_message}\n\n Additional Info:\n{" " * 4}{message}'
|
|
12
|
+
super().__init__(message)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StreamSourceUnreadableError(MatrixStreamError):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__('The source is not readable.')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LED Matrix Module.
|
|
3
|
+
|
|
4
|
+
This module provides a high-level interface for interacting with LED matrix devices.
|
|
5
|
+
It combines functionality from various modules to provide a simple API for controlling
|
|
6
|
+
LED matrices, displaying patterns, animations, and battery status.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
# Standard library
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Optional, Union, List
|
|
14
|
+
|
|
15
|
+
# Third-party
|
|
16
|
+
from serial.tools.list_ports_common import ListPortInfo
|
|
17
|
+
|
|
18
|
+
# Inspyre-Softworks
|
|
19
|
+
from is_matrix_forge.led_matrix.display.grid import Grid, generate_blank_grid
|
|
20
|
+
from is_matrix_forge.led_matrix.display.animations import goodbye_animation
|
|
21
|
+
from is_matrix_forge.led_matrix.helpers.device import get_devices
|
|
22
|
+
from is_matrix_forge.common.helpers import percentage_to_value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Taylor B. tayjaybabee@gmail.com
|
|
3
|
+
Date: 2025-05-12 22:17:04
|
|
4
|
+
LastEditors: Taylor B. tayjaybabee@gmail.com
|
|
5
|
+
LastEditTime: 2025-05-12 22:17:28
|
|
6
|
+
FilePath: is_matrix_forge/led_matrix/tools/__init__.py
|
|
7
|
+
Description: 这是默认设置,可以在设置》工具》File Description中进行配置
|
|
8
|
+
"""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from is_matrix_forge.led_matrix import LEDMatrixController
|
|
2
|
+
from is_matrix_forge.led_matrix.helpers.device import DEVICES
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
threads = []
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def identify_devices(devices=None):
|
|
10
|
+
global threads
|
|
11
|
+
|
|
12
|
+
if devices is None:
|
|
13
|
+
devices = DEVICES
|
|
14
|
+
|
|
15
|
+
controllers = []
|
|
16
|
+
|
|
17
|
+
for device in devices:
|
|
18
|
+
controllers.append(LEDMatrixController(device, 100))
|
|
19
|
+
|
|
20
|
+
for controller in controllers:
|
|
21
|
+
t = Thread(target=controller.identify)
|
|
22
|
+
threads.append(t)
|
|
23
|
+
t.start()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
identify_devices()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
Author:
|
|
5
|
+
Inspyre Softworks
|
|
6
|
+
|
|
7
|
+
Project:
|
|
8
|
+
led-matrix-battery
|
|
9
|
+
|
|
10
|
+
File:
|
|
11
|
+
is_matrix_forge/led_matrix/utils/__init__.py
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Description:
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
import serial
|
|
19
|
+
|
|
20
|
+
from is_matrix_forge.led_matrix import RESPONSE_SIZE, disconnect_dev, FWK_MAGIC
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def send_command_raw(dev, command, with_response=False):
|
|
24
|
+
"""Send a command to the device.
|
|
25
|
+
Opens new serial connection every time"""
|
|
26
|
+
# print(f"Sending command: {command}")
|
|
27
|
+
try:
|
|
28
|
+
with serial.Serial(dev.device, 115200) as s:
|
|
29
|
+
s.write(command)
|
|
30
|
+
|
|
31
|
+
if with_response:
|
|
32
|
+
res = s.read(RESPONSE_SIZE)
|
|
33
|
+
# print(f"Received: {res}")
|
|
34
|
+
return res
|
|
35
|
+
except (IOError, OSError) as _ex:
|
|
36
|
+
disconnect_dev(dev.device)
|
|
37
|
+
# print("Error: ", ex)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def send_command(dev, command, parameters=[], with_response=False):
|
|
41
|
+
return send_command_raw(dev, FWK_MAGIC + [command] + parameters, with_response)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from inspy_logger import InspyLogger, Loggable
|
|
2
|
+
from inspy_logger.constants import LEVEL_MAP
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LOG_LEVELS = [level for level in LEVEL_MAP.keys()]
|
|
6
|
+
|
|
7
|
+
del LEVEL_MAP
|
|
8
|
+
|
|
9
|
+
PROGNAME = 'LEDMatrixBattery'
|
|
10
|
+
AUTHOR = 'Inspyre-Softworks'
|
|
11
|
+
|
|
12
|
+
INSPY_LOG_LEVEL = 'INFO'
|
|
13
|
+
|
|
14
|
+
ROOT_LOGGER = InspyLogger(PROGNAME, console_level='info', no_file_logging=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'AUTHOR',
|
|
19
|
+
'INSPY_LOG_LEVEL',
|
|
20
|
+
'Loggable',
|
|
21
|
+
'LOG_LEVELS',
|
|
22
|
+
'PROGNAME',
|
|
23
|
+
'ROOT_LOGGER',
|
|
24
|
+
]
|
|
25
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File: monitor.py
|
|
3
|
+
Author: Inspyre-Softworks
|
|
4
|
+
Description:
|
|
5
|
+
This file contains the PowerMonitor class, which is used to monitor the battery level of the system.
|
|
6
|
+
"""
|
|
7
|
+
# Standard library imports
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from threading import Thread
|
|
10
|
+
from typing import Union, Optional
|
|
11
|
+
|
|
12
|
+
# Third-party imports
|
|
13
|
+
from serial.tools.list_ports_common import ListPortInfo
|
|
14
|
+
|
|
15
|
+
# Inspyre-Softworks imports
|
|
16
|
+
from easy_exit_calls import ExitCallHandler
|
|
17
|
+
|
|
18
|
+
# Package imports
|
|
19
|
+
from .errors import *
|
|
20
|
+
from .helpers import get_plugged_status
|
|
21
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER as PARENT_LOGGER
|
|
22
|
+
from is_matrix_forge.notify.sounds import PLUGGED_NOTIFY, UNPLUGGED_NOTIFY
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
DEFAULT_PLUGGED_SOUND = PLUGGED_NOTIFY
|
|
26
|
+
DEFAULT_UNPLUGGED_SOUND = UNPLUGGED_NOTIFY
|
|
27
|
+
|
|
28
|
+
ECH = ExitCallHandler()
|
|
29
|
+
|
|
30
|
+
MOD_LOGGER = PARENT_LOGGER.get_child('monitor')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def run_power_monitor(
|
|
34
|
+
device: ListPortInfo,
|
|
35
|
+
battery_check_interval: int = 5,
|
|
36
|
+
plugged_alert: Optional[Union[str, Path]] = DEFAULT_PLUGGED_SOUND,
|
|
37
|
+
unplugged_alert: Optional[Union[str, Path]] = DEFAULT_UNPLUGGED_SOUND,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Run the power monitor.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
device (ListPortInfo):
|
|
44
|
+
The LED matrix on which to display the battery level.
|
|
45
|
+
|
|
46
|
+
battery_check_interval (Optional[Union[int, float, str]]):
|
|
47
|
+
The interval (in seconds) at which to check the battery level.
|
|
48
|
+
|
|
49
|
+
plugged_alert (Optional[Union[str, Path]]):
|
|
50
|
+
The filepath to the sound to play when the device is plugged into power.
|
|
51
|
+
|
|
52
|
+
unplugged_alert (Optional[Union[str, Path]]):
|
|
53
|
+
The filepath to the sound to play when the device is unplugged from power.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
monitor = PowerMonitor(
|
|
57
|
+
device,
|
|
58
|
+
battery_check_interval=battery_check_interval,
|
|
59
|
+
plugged_alert=plugged_alert,
|
|
60
|
+
unplugged_alert=unplugged_alert
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
monitor.start()
|
|
65
|
+
except KeyboardInterrupt:
|
|
66
|
+
monitor.stop(without_salutation=True)
|
|
67
|
+
|
|
68
|
+
return monitor
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from inspyre_toolbox.exceptional import CustomRootException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PowerMonitorError(CustomRootException):
|
|
5
|
+
"""
|
|
6
|
+
Base class for all PowerMonitor errors.
|
|
7
|
+
|
|
8
|
+
Class Attributes:
|
|
9
|
+
default_message (str): Read-Only. The default message for the error.
|
|
10
|
+
"""
|
|
11
|
+
default_message = 'An error occurred while monitoring the battery!'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PowerMonitorNotRunningError(PowerMonitorError):
|
|
15
|
+
"""
|
|
16
|
+
Raised when a method is called on a PowerMonitor instance that is not running, but should be to perform the action.
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, message = None):
|
|
19
|
+
msg = 'The PowerMonitor is not running, can not perform action on a non-running instance!'
|
|
20
|
+
if message is not None:
|
|
21
|
+
msg += f'\n\nAdditional information from caller: {message}'
|
|
22
|
+
|
|
23
|
+
super().__init__(msg)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BatteryStateUnknownError(PowerMonitorError):
|
|
27
|
+
"""
|
|
28
|
+
Raised when the state of the battery cannot be determined.
|
|
29
|
+
"""
|
|
30
|
+
def __init__(self, message = None):
|
|
31
|
+
msg = 'The state of the battery cannot be determined!'
|
|
32
|
+
if message is not None:
|
|
33
|
+
msg += f'\n\nAdditional information from caller: {message}'
|
|
34
|
+
|
|
35
|
+
super().__init__(msg)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER
|
|
2
|
+
from is_matrix_forge.monitor.monitor import PowerMonitor
|
|
3
|
+
from is_matrix_forge.monitor.helpers import get_battery_percentage
|
|
4
|
+
|
|
5
|
+
MOD_LOGGER = ROOT_LOGGER.get_child('monitor.events')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def event_to_bool(event: str) -> bool:
|
|
9
|
+
"""
|
|
10
|
+
Converts an event to a boolean.
|
|
11
|
+
|
|
12
|
+
Parameters:
|
|
13
|
+
event:
|
|
14
|
+
The string representation of the event to convert.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
bool:
|
|
18
|
+
The converted boolean value.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError:
|
|
22
|
+
If the event is not a valid boolean value.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
>>> event_to_bool('true')
|
|
26
|
+
True
|
|
27
|
+
>>> event_to_bool('false')
|
|
28
|
+
False
|
|
29
|
+
>>> event_to_bool('invalid')
|
|
30
|
+
Traceback (most recent call last):
|
|
31
|
+
...
|
|
32
|
+
ValueError: invalid is not a valid boolean value.
|
|
33
|
+
"""
|
|
34
|
+
event = event.lower().strip()
|
|
35
|
+
|
|
36
|
+
if event in ['on', 'true', 'plugged']:
|
|
37
|
+
return True
|
|
38
|
+
elif event in ['off', 'false', 'unplugged']:
|
|
39
|
+
return False
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(f'{event} is not a valid boolean value.')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __handle_device_plugged_in(power_monitor):
|
|
45
|
+
pm = power_monitor
|
|
46
|
+
if pm.plugged_in and not pm.controller.is_animating and (not pm.last_state or pm.last_state is None):
|
|
47
|
+
pm.notify('plugged')
|
|
48
|
+
pm.controller.clear()
|
|
49
|
+
pm._last_state = True
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def __handle_device_unplugged(power_monitor):
|
|
53
|
+
pm = power_monitor
|
|
54
|
+
if pm.unplugged and not pm.controller.is_animating and (pm.last_state or pm.last_state is None):
|
|
55
|
+
pm.notify('unplugged')
|
|
56
|
+
pm.controller.clear()
|
|
57
|
+
pm._last_state = False
|
|
58
|
+
|
|
59
|
+
pm.controller.draw_percentage(get_battery_percentage())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def handle_event(event: str, power_monitor: PowerMonitor):
|
|
63
|
+
log = MOD_LOGGER.get_child('handle_event')
|
|
64
|
+
log.debug(f'Handling {event}...')
|
|
65
|
+
|
|
66
|
+
#if not isinstance(power_monitor, PowerMonitor):
|
|
67
|
+
# raise TypeError(f'{power_monitor} is not a PowerMonitor instance.')
|
|
68
|
+
#else:
|
|
69
|
+
# log.debug(f'{power_monitor} is a PowerMonitor instance.')
|
|
70
|
+
|
|
71
|
+
event = event_to_bool(event)
|
|
72
|
+
|
|
73
|
+
pm = power_monitor
|
|
74
|
+
|
|
75
|
+
if event:
|
|
76
|
+
__handle_device_plugged_in(pm)
|
|
77
|
+
else:
|
|
78
|
+
__handle_device_unplugged(pm)
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from inspyre_toolbox.exceptional import CustomRootException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PowerMonitorGUIError(CustomRootException):
|
|
5
|
+
"""
|
|
6
|
+
Base class for all PowerMonitor GUI errors.
|
|
7
|
+
|
|
8
|
+
Class Attributes:
|
|
9
|
+
default_message (str): Read-Only. The default message for the error.
|
|
10
|
+
"""
|
|
11
|
+
default_message = 'An error occurred with the PowerMonitor GUI!'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MalformedWindowError(PowerMonitorGUIError):
|
|
15
|
+
"""
|
|
16
|
+
Raised when a window is malformed or not properly initialized.
|
|
17
|
+
|
|
18
|
+
Class Attributes:
|
|
19
|
+
default_message (str): Read-Only. The default message for the error.
|
|
20
|
+
"""
|
|
21
|
+
default_message = 'Window is malformed or not properly initialized!'
|
|
22
|
+
|
|
23
|
+
def __init__(self, message: str = None, **kwargs) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Initializes the MalformedWindowError.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
message (Optional[str]):
|
|
29
|
+
The error message to be displayed. If None, uses the default message.
|
|
30
|
+
"""
|
|
31
|
+
if message is not None:
|
|
32
|
+
self.default_message = f"{self.default_message}\n\n Additional information from caller:\n {message}"
|
|
33
|
+
|
|
34
|
+
super().__init__(message=self.default_message, **kwargs)
|
|
35
|
+
|