2024-11-11 15:22:09 +01:00
import json
from threading import Timer
import bdfparser
2024-11-11 16:49:43 +01:00
from sense_hat import ACTION_RELEASED , SenseHat
2024-11-11 15:22:09 +01:00
class Display :
def __init__ ( self , mqttc , uid , name ) :
self . mqttc = mqttc
self . sense = SenseHat ( )
self . uid = uid
self . name = name
self . init_lights ( )
self . mqttc . will_set ( self . availability_topic , " offline " , retain = True )
@property
def device ( self ) :
return {
" identifiers " : [ self . uid ] ,
" name " : self . name ,
}
@property
def availability_topic ( self ) :
return f " { self . uid } /display/availability "
def init_lights ( self ) :
options = {
" device " : self . device ,
" availability_topic " : self . availability_topic ,
}
self . main_light = Light ( " LED " , self . uid , " led " , self . sense , * * options )
def publish_discovery ( self ) :
self . main_light . publish_discovery ( self . mqttc )
def publish_online ( self ) :
self . subscribe ( )
self . mqttc . publish ( self . availability_topic , " online " , retain = True )
def subscribe ( self ) :
self . main_light . subscribe ( self . mqttc )
def on_message ( self , * args , * * kwargs ) :
self . main_light . on_message ( * args , * * kwargs )
class Light :
2024-11-11 16:49:43 +01:00
def __init__ ( self , name , parent_uid , slug , sense , n = 3 , * * kwargs ) :
2024-11-11 15:22:09 +01:00
self . name = name
self . parent_uid = parent_uid
self . slug = slug
self . sense = sense
self . options = kwargs
self . _switch = False
2024-11-11 16:49:43 +01:00
self . _color = [ [ 255 , 255 , 255 ] ] * n
self . _pres = [ " " ] * n
self . _values = [ " " ] * n
self . _n = n
self . _i = 0
2024-11-11 15:22:09 +01:00
self . font = bdfparser . Font ( " src/tom-thumb.bdf " )
self . timer = Timer ( 0 , self . __init__ )
2024-11-11 16:49:43 +01:00
self . sense . stick . direction_right = self . switch_screen
self . sense . stick . direction_left = self . switch_screen_rev
2024-11-11 15:22:09 +01:00
def publish_discovery ( self , mqttc ) :
2024-11-11 16:49:43 +01:00
for i in range ( self . _n ) :
mqttc . publish (
self . get_discovery_topic ( i ) ,
json . dumps (
{
" command_topic " : self . command_topic ( i , " command " ) ,
" effect_command_topic " : self . command_topic ( i , " effect " ) ,
" effect_list " : [ " Low Light " , " Normal " ] ,
" effect_state_topie " : self . state_topic ,
" effect_value_template " : " {{ value_json.effect }} " ,
" icon " : " mdi:dots-grid " ,
" name " : f " { self . name } { i } " ,
" on_command_type " : " brightness " ,
" rgb_command_topic " : self . command_topic ( i , " command " ) ,
" rgb_state_topic " : self . state_topic ,
" rgb_value_template " : " {{ " + f " value_json.rgb[ { i } ] " + " }} " ,
" retain " : True ,
" unique_id " : f " { self . uid } _ { i } " ,
" state_topic " : self . state_topic ,
" state_value_template " : " {{ value_json.state }} " ,
}
| self . options
) ,
retain = True ,
)
self . publish_state ( mqttc )
2024-11-11 15:22:09 +01:00
def subscribe ( self , mqttc ) :
2024-11-11 16:49:43 +01:00
for i in range ( self . _n ) :
mqttc . subscribe ( self . command_topic ( i , " command " ) )
mqttc . subscribe ( self . command_topic ( i , " effect " ) )
mqttc . subscribe ( self . command_topic ( i , " rgb " ) )
mqttc . subscribe ( self . command_topic ( i , " value " ) )
2024-11-11 15:22:09 +01:00
def on_message ( self , client , userdata , message ) :
data = message . payload . decode ( )
2024-11-11 16:49:43 +01:00
match message . topic . rsplit ( " / " , maxsplit = 2 ) :
case [ self . base_topic , i , " command " ] :
match data . split ( " , " ) :
case [ " OFF " ] :
self . switch = False
case [ * rgb ] :
self . set_color ( int ( i ) , list ( map ( int , rgb ) ) )
case [ self . base_topic , i , " effect " ] :
2024-11-11 15:22:09 +01:00
self . sense . low_light = data == " Low Light "
2024-11-11 16:49:43 +01:00
case [ self . base_topic , i , " value " ] :
self . set_value ( int ( i ) , data )
2024-11-11 15:22:09 +01:00
case _ :
return
self . publish_state ( client )
def publish_state ( self , mqttc ) :
mqttc . publish (
self . state_topic ,
json . dumps (
{
" effect " : " Low Light " if self . sense . low_light else " Normal " ,
" rgb " : self . rgb ,
" state " : self . state ,
}
) ,
retain = True ,
)
@property
def uid ( self ) :
return f " { self . parent_uid } _ { self . slug } "
2024-11-11 16:49:43 +01:00
def get_discovery_topic ( self , i ) :
return f " homeassistant/light/ { self . uid } _ { i } /config "
2024-11-11 15:22:09 +01:00
@property
def base_topic ( self ) :
return f " { self . parent_uid } /display/ { self . slug } "
2024-11-11 16:49:43 +01:00
def command_topic ( self , i , cmd ) :
return f " { self . base_topic } / { i } / { cmd } "
2024-11-11 15:22:09 +01:00
@property
def state_topic ( self ) :
return f " { self . parent_uid } /display/ { self . slug } /state "
@property
def switch ( self ) :
return self . _switch
@switch.setter
def switch ( self , value ) :
self . _switch = value
if value :
self . update_value ( )
else :
self . sense . clear ( )
@property
def state ( self ) :
return " ON " if self . switch else " OFF "
@property
def color ( self ) :
return self . _color
2024-11-11 16:49:43 +01:00
def set_color ( self , i , value ) :
self . _color [ i ] = value
2024-11-11 15:22:09 +01:00
if not self . switch :
self . switch = True
2024-11-11 16:49:43 +01:00
if i == self . _i :
self . display_value ( )
2024-11-11 15:22:09 +01:00
@property
def rgb ( self ) :
2024-11-11 16:49:43 +01:00
return [ " , " . join ( map ( str , self . color [ i ] ) ) for i in range ( self . _n ) ]
2024-11-11 15:22:09 +01:00
@property
def value ( self ) :
2024-11-11 16:49:43 +01:00
return f " { self . _pres [ self . _i ] } { self . _values [ self . _i ] } "
2024-11-11 15:22:09 +01:00
2024-11-11 16:49:43 +01:00
def set_value ( self , i , value ) :
2024-11-11 15:22:09 +01:00
match value . split ( ) :
case [ val ] :
2024-11-11 16:49:43 +01:00
self . _pres [ i ] = " "
self . _values [ i ] = val
2024-11-11 15:22:09 +01:00
case [ pre , val , * _ ] :
2024-11-11 16:49:43 +01:00
self . _pres [ i ] = pre
self . _values [ i ] = val
if i == self . _i :
self . display_value ( )
2024-11-11 15:22:09 +01:00
def update_value ( self ) :
if not self . switch :
return
2024-11-11 16:49:43 +01:00
if not self . _pres [ self . _i ] :
2024-11-11 15:22:09 +01:00
self . display_value ( )
return
self . timer . cancel ( )
2024-11-11 16:49:43 +01:00
pixels = self . to_pixels ( self . _pres [ self . _i ] )
2024-11-11 15:22:09 +01:00
self . sense . set_pixels ( pixels )
2024-11-11 16:49:43 +01:00
self . timer = Timer ( 0.25 , self . display_value , kwargs = dict ( timer = True ) )
2024-11-11 15:22:09 +01:00
self . timer . start ( )
def display_value ( self , timer = False ) :
if ( not timer and self . timer . is_alive ( ) ) or not self . switch :
return
2024-11-11 16:49:43 +01:00
pixels = self . to_pixels ( self . _values [ self . _i ] )
2024-11-11 15:22:09 +01:00
self . sense . set_pixels ( pixels )
def to_pixels ( self , text ) :
if text :
return [
2024-11-11 16:49:43 +01:00
self . color [ self . _i ] if x else [ 0 , 0 , 0 ]
2024-11-11 15:22:09 +01:00
for x in self . font . draw ( text ) . crop ( 8 , 8 , yoff = - 2 ) . todata ( 3 )
]
2024-11-11 16:49:43 +01:00
return [ self . color [ self . _i ] ] * 64
def switch_screen ( self , event ) :
if event . action == ACTION_RELEASED :
self . _i = ( self . _i + 1 ) % self . _n
self . update_value ( )
def switch_screen_rev ( self , event ) :
if event . action == ACTION_RELEASED :
self . _i = ( self . _i - 1 ) % self . _n
self . update_value ( )