2023-11-22 15:21:15 +00:00
#!/usr/bin/env python
2024-02-16 08:15:01 +00:00
2024-03-13 12:27:54 +00:00
import os
2023-11-22 15:44:26 +00:00
from deps . samplebase import SampleBase
2023-11-22 15:21:15 +00:00
from rgbmatrix import graphics
2024-02-15 08:27:19 +00:00
from PIL import Image
2024-02-16 06:12:12 +00:00
import base64
import io
2023-11-23 07:15:02 +00:00
2023-11-23 09:07:17 +00:00
from flask import Flask , request , jsonify
2023-11-23 13:31:18 +00:00
from flask_cors import CORS , cross_origin
2024-03-13 12:03:09 +00:00
from dotenv import load_dotenv
2023-11-23 07:49:47 +00:00
from waitress import serve
2024-03-13 12:03:09 +00:00
from pathlib import Path
2023-11-23 07:49:47 +00:00
import time , threading
2023-11-22 14:41:13 +00:00
2023-11-23 07:15:02 +00:00
2024-03-13 12:03:09 +00:00
env_path = Path ( ' .. ' ) / ' .env '
load_dotenv ( dotenv_path = env_path )
2023-11-23 09:07:17 +00:00
api = Flask ( __name__ )
2023-11-23 13:31:18 +00:00
cors = CORS ( api )
2023-11-23 07:15:02 +00:00
2024-02-15 08:43:38 +00:00
@api.route ( ' /upload ' , methods = [ ' POST ' ] )
2024-02-15 08:27:19 +00:00
@cross_origin ( )
def set_image ( ) :
# prepare response data
response = { ' success ' : True }
try :
global matrix
assert matrix is not None
# receive client data
data = request . form
# try unpacking text and x,y coordinates
url = data . get ( ' url ' )
# call matrix method with data
matrix . set_image ( url )
except Exception as e :
# error handling
print ( e )
response [ ' success ' ] = False
# respond to client
return jsonify ( response )
2024-02-15 08:43:38 +00:00
@api.route ( ' /image ' , methods = [ ' POST ' ] )
2024-02-15 08:27:19 +00:00
@cross_origin ( )
def display_image ( ) :
# prepare response data
response = { ' success ' : True }
try :
global matrix
assert matrix is not None
# receive client data
data = request . form
# try unpacking x,y coordinates
x_coord = int ( data . get ( ' x ' ) )
y_coord = int ( data . get ( ' y ' ) )
# call matrix method with data
2024-02-16 06:11:50 +00:00
matrix . display_image ( x_coord , y_coord )
2024-02-15 08:27:19 +00:00
except Exception as e :
# error handling
print ( e )
response [ ' success ' ] = False
# respond to client
return jsonify ( response )
2023-11-23 09:07:41 +00:00
@api.route ( ' /text ' , methods = [ ' POST ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:07:41 +00:00
def display_text ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:07:41 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 09:07:41 +00:00
2023-11-23 10:45:56 +00:00
# receive client data
2023-11-24 06:06:43 +00:00
data = request . form
2023-11-23 10:45:56 +00:00
# try unpacking text and x,y coordinates
2023-11-23 09:07:41 +00:00
text = data . get ( ' text ' )
2023-11-24 06:06:43 +00:00
x_coord = int ( data . get ( ' x ' ) )
y_coord = int ( data . get ( ' y ' ) )
2023-11-23 09:07:41 +00:00
2023-11-23 10:45:56 +00:00
# call matrix method with data
2023-11-23 09:07:41 +00:00
matrix . text ( x_coord , y_coord , text )
2023-11-23 09:44:57 +00:00
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:44:57 +00:00
print ( e )
2023-11-23 09:07:41 +00:00
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:07:41 +00:00
return jsonify ( response )
2023-11-23 09:46:15 +00:00
@api.route ( ' /pixel ' , methods = [ ' POST ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:46:15 +00:00
def set_pixel ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:46:15 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 09:46:15 +00:00
2023-11-23 10:45:56 +00:00
# receive client data
2023-11-24 06:06:43 +00:00
data = request . form
2023-11-23 10:45:56 +00:00
# try unpacking x,y coordinates
2023-11-24 06:06:43 +00:00
x_coord = int ( data . get ( ' x ' ) )
y_coord = int ( data . get ( ' y ' ) )
2023-11-23 09:46:15 +00:00
2023-11-23 10:45:56 +00:00
# call matrix method with data
2023-11-23 09:46:15 +00:00
matrix . pixel ( x_coord , y_coord )
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:46:15 +00:00
print ( e )
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:46:15 +00:00
return jsonify ( response )
2023-11-23 09:47:40 +00:00
@api.route ( ' /circle ' , methods = [ ' POST ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:47:40 +00:00
def draw_circle ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:47:40 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 09:47:40 +00:00
2023-11-23 10:45:56 +00:00
# receive client data
2023-11-24 06:06:43 +00:00
data = request . form
2023-11-23 10:45:56 +00:00
# try unpacking radius and x,y coordinates
2023-11-24 06:06:43 +00:00
radius = int ( data . get ( ' r ' ) )
x_coord = int ( data . get ( ' x ' ) )
y_coord = int ( data . get ( ' y ' ) )
2023-11-23 09:47:40 +00:00
2023-11-23 10:45:56 +00:00
# call matrix method with data
2023-11-23 09:47:40 +00:00
matrix . circle ( x_coord , y_coord , radius )
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:47:40 +00:00
print ( e )
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:47:40 +00:00
return jsonify ( response )
2023-11-23 09:48:19 +00:00
@api.route ( ' /rectangle ' , methods = [ ' POST ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:48:19 +00:00
def draw_rectangle ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:48:19 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 09:48:19 +00:00
2023-11-23 10:45:56 +00:00
# receive client data
2023-11-24 06:06:43 +00:00
data = request . form
2023-11-23 10:45:56 +00:00
# try unpacking rectangle width and height
2023-11-24 06:06:43 +00:00
width = int ( data . get ( ' w ' ) )
height = int ( data . get ( ' h ' ) )
2023-11-23 10:45:56 +00:00
# try unpacking x,y coordinates
2023-11-24 06:06:43 +00:00
x_coord = int ( data . get ( ' x ' ) )
y_coord = int ( data . get ( ' y ' ) )
2023-11-23 09:48:19 +00:00
2023-11-23 10:45:56 +00:00
# call matrix method with data
2023-11-23 09:48:19 +00:00
matrix . rectangle ( x_coord , y_coord , width , height )
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:48:19 +00:00
print ( e )
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:48:19 +00:00
return jsonify ( response )
2024-03-08 10:23:40 +00:00
@api.route ( ' /line ' , methods = [ ' POST ' ] )
@cross_origin ( )
def draw_line ( ) :
# prepare response data
response = { ' success ' : True }
try :
global matrix
assert matrix is not None
# receive client data
data = request . form
# try unpacking x,y coordinates
x1 = int ( data . get ( ' x1 ' ) )
y1 = int ( data . get ( ' y1 ' ) )
x2 = int ( data . get ( ' x2 ' ) )
y2 = int ( data . get ( ' y2 ' ) )
# call matrix method with data
matrix . line ( x1 , y1 , x2 , y2 )
except Exception as e :
# error handling
print ( e )
response [ ' success ' ] = False
# respond to client
return jsonify ( response )
2023-11-23 09:46:43 +00:00
@api.route ( ' /color ' , methods = [ ' POST ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:46:43 +00:00
def set_color ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:46:43 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 09:46:43 +00:00
2023-11-23 10:45:56 +00:00
# receive client data
2023-11-24 06:06:43 +00:00
data = request . form
2023-11-23 10:45:56 +00:00
# try unpacking red, green and blue color values
2023-11-24 06:06:43 +00:00
red = int ( data . get ( ' r ' ) )
green = int ( data . get ( ' g ' ) )
blue = int ( data . get ( ' b ' ) )
2023-11-23 09:46:43 +00:00
2023-11-23 10:45:56 +00:00
# call matrix method with data
2023-11-23 09:46:43 +00:00
matrix . set_color ( red , green , blue )
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:46:43 +00:00
print ( e )
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:46:43 +00:00
return jsonify ( response )
2023-11-23 09:49:03 +00:00
@api.route ( ' /clear ' , methods = [ ' GET ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 13:03:55 +00:00
def clear_canvas ( ) :
2023-11-23 10:45:56 +00:00
# prepare response data
2023-11-23 09:49:03 +00:00
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-23 10:45:56 +00:00
2023-11-24 06:06:43 +00:00
# clear matrix canvas
2023-11-23 09:49:03 +00:00
matrix . clear ( )
except Exception as e :
2023-11-23 10:45:56 +00:00
# error handling
2023-11-23 09:49:03 +00:00
print ( e )
response [ ' success ' ] = False
2024-02-15 08:27:05 +00:00
# respond to client
2023-11-23 09:49:03 +00:00
return jsonify ( response )
2023-11-23 09:07:41 +00:00
@api.route ( ' /update ' , methods = [ ' GET ' ] )
2023-11-23 13:31:18 +00:00
@cross_origin ( )
2023-11-23 09:07:41 +00:00
def update_matrix ( ) :
response = { ' success ' : True }
try :
global matrix
2024-02-15 08:27:05 +00:00
assert matrix is not None
2023-11-24 06:06:43 +00:00
# swap matrix canvas
2023-11-23 09:07:41 +00:00
matrix . update ( )
2023-11-23 09:44:57 +00:00
except Exception as e :
print ( e )
2023-11-23 09:07:41 +00:00
response [ ' success ' ] = False
2023-11-23 10:45:56 +00:00
# respond to client
2023-11-23 09:07:41 +00:00
return jsonify ( response )
2023-11-23 07:49:47 +00:00
2023-11-23 07:15:02 +00:00
2023-11-23 10:45:56 +00:00
# derived class to access rgb-led-matrix
2023-11-23 09:07:17 +00:00
class Matrix ( SampleBase ) :
2023-11-22 15:22:19 +00:00
def __init__ ( self , * args , * * kwargs ) :
2023-11-23 09:07:17 +00:00
super ( Matrix , self ) . __init__ ( * args , * * kwargs )
2024-03-13 12:27:54 +00:00
self . parser . add_argument ( " -r " , " --led-rows " , action = " store " , help = " Display rows. 16 for 16x32, 32 for 32x32. Default: 64 " , default = int ( os . getenv ( ' PUBLIC_LED_WIDTH ' , 64 ) ) , type = int )
self . parser . add_argument ( " --led-cols " , action = " store " , help = " Panel columns. Typically 32 or 64. (Default: 64) " , default = int ( os . getenv ( ' PUBLIC_LED_PARALLEL ' , 64 ) ) , type = int )
self . parser . add_argument ( " -c " , " --led-chain " , action = " store " , help = " Daisy-chained boards. Default: 1. " , default = int ( os . getenv ( ' PUBLIC_LED_CHAIN ' , 1 ) ) , type = int )
self . parser . add_argument ( " -P " , " --led-parallel " , action = " store " , help = " For Plus-models or RPi2: parallel chains. 1..3. Default: 1 " , default = int ( os . getenv ( ' PUBLIC_LED_PARALLEL ' , 1 ) ) , type = int )
2023-11-22 14:41:13 +00:00
2023-11-23 10:45:56 +00:00
# run display program
2023-11-22 15:22:19 +00:00
def run ( self ) :
2023-11-23 10:45:56 +00:00
# initialize font
2023-11-23 07:49:47 +00:00
self . font = graphics . Font ( )
self . font . LoadFont ( " deps/fonts/9x18B.bdf " )
2023-11-23 10:45:56 +00:00
# initialize color
2023-11-23 07:49:47 +00:00
self . color = graphics . Color ( 255 , 255 , 255 )
2024-02-15 08:27:19 +00:00
# initialize image
self . image = None
2023-11-23 09:07:17 +00:00
2023-11-23 10:45:56 +00:00
# initialize canvas
2023-11-23 07:49:47 +00:00
self . canvas = self . matrix . CreateFrameCanvas ( )
2023-11-23 09:07:17 +00:00
self . canvas . Clear ( )
2023-11-22 14:41:13 +00:00
2023-11-23 10:45:56 +00:00
# wait in idle
2023-11-22 15:21:15 +00:00
while True :
2023-11-23 07:49:47 +00:00
time . sleep ( .05 )
2023-11-23 07:15:02 +00:00
2023-11-23 10:45:56 +00:00
# clear canvas
2023-11-23 09:44:57 +00:00
def clear ( self ) :
self . canvas . Clear ( )
2023-11-23 10:45:56 +00:00
# swap canvas with buffer
2023-11-23 07:49:47 +00:00
def update ( self ) :
self . canvas = self . matrix . SwapOnVSync ( self . canvas )
2023-11-23 09:44:57 +00:00
self . clear ( )
2023-11-23 09:07:17 +00:00
2023-11-23 10:45:56 +00:00
# set current color
2023-11-24 06:06:43 +00:00
def set_color ( self , r : int , g : int , b : int ) :
2023-11-23 09:46:15 +00:00
self . color = graphics . Color ( r , g , b )
2023-11-23 10:45:56 +00:00
# set specified pixel to current color
2023-11-24 06:06:43 +00:00
def pixel ( self , x : int , y : int ) :
2023-11-23 09:46:15 +00:00
self . canvas . SetPixel ( x , y , self . color . red , self . color . green , self . color . blue )
2023-11-23 10:45:56 +00:00
# display text at position with current color
2023-11-24 06:06:43 +00:00
def text ( self , x : int , y : int , t : int ) :
2023-11-23 09:07:17 +00:00
graphics . DrawText ( self . canvas , self . font , x * 9 , ( y + 1 ) * 18 , self . color , t )
2023-11-22 14:41:13 +00:00
2023-11-23 10:45:56 +00:00
# display circle at position with radius in current color
2023-11-24 06:06:43 +00:00
def circle ( self , x : int , y : int , r : int ) :
2023-11-24 06:09:04 +00:00
graphics . DrawCircle ( self . canvas , x , y , r , self . color )
2023-11-23 09:47:40 +00:00
2023-11-23 10:45:56 +00:00
# display rectangle at position with dimensions in current color
2023-11-24 06:06:43 +00:00
def rectangle ( self , x : int , y : int , w : int , h : int ) :
2023-11-23 10:45:56 +00:00
# loop through each point in dimensions
2023-11-23 09:48:19 +00:00
for i in range ( w ) :
for j in range ( h ) :
2023-11-23 10:45:56 +00:00
# set next pixel in rectangle
2023-11-23 09:48:19 +00:00
self . canvas . SetPixel ( x + i , y + j , self . color . red , self . color . green , self . color . blue )
2024-03-08 10:23:40 +00:00
# display line from (x1,y1) to (x2,y2) in current color
def line ( self , x1 : int , y1 : int , x2 : int , y2 : int ) :
graphics . DrawLine ( self . canvas , x1 , y1 , x2 , y2 , self . color )
2024-02-15 08:27:19 +00:00
# set current image
def set_image ( self , url : str ) :
2024-02-16 06:12:12 +00:00
_ , data = url . split ( ' , ' )
data = base64 . b64decode ( data )
self . image = Image . open ( io . BytesIO ( data ) ) . convert ( ' RGB ' )
2024-02-15 08:27:19 +00:00
2024-02-15 08:31:34 +00:00
# display image at position with dimensions in current color
2024-02-16 06:11:50 +00:00
def display_image ( self , x : int , y : int ) :
if self . image is None : return
2024-02-15 08:27:19 +00:00
# set image at position
self . canvas . SetImage ( self . image , x , y )
2023-11-22 15:21:15 +00:00
2023-11-23 10:45:56 +00:00
# initialize global matrix variable
2023-11-23 09:07:17 +00:00
matrix = None
2023-11-22 15:21:15 +00:00
if __name__ == " __main__ " :
2023-11-23 10:45:56 +00:00
# set matrix to instance
2023-11-23 09:07:17 +00:00
matrix = Matrix ( )
2023-11-23 10:45:56 +00:00
# run rest api in thread, serving with 'waitress'
2023-11-23 09:07:17 +00:00
threading . Thread ( target = lambda : serve ( api , host = " 0.0.0.0 " , port = 8080 ) ) . start ( )
2023-11-23 07:15:02 +00:00
2023-11-23 10:45:56 +00:00
# help menu on call
2023-11-23 09:07:17 +00:00
if not matrix . process ( ) :
matrix . print_help ( )