Save RAM and improve volume changes

Save RAM by optimising imports and reducing class usage
Change volume by 8% each time, using saved volume value and only one query. Minimum volume is 1% and max is 100%. Queries are not run if decreasing volume from 1% or increasing from 100%
This commit is contained in:
2024-05-16 22:55:51 +01:00
parent 0c4cbfcbb1
commit ae8e42d68f
6 changed files with 214 additions and 231 deletions

74
api.py
View File

@@ -1,43 +1,37 @@
from requests import get, post from requests import get, post
import gc from gc import collect
import time from time import mktime, localtime
from env import HASS_URL, TOKEN
class Api: def getReq(endpoint) -> dict:
def __init__(self, base_url, access_token) -> None: collect()
self.base_url = base_url
self.access_token = access_token
def __request(self, endpoint) -> dict:
gc.collect()
url = self.base_url + endpoint
headers = { headers = {
"Authorization": "Bearer " + self.access_token, "Authorization": "Bearer " + TOKEN,
"content-type": "application/json" "content-type": "application/json"
} }
print("Starting request for " + endpoint) print("Starting request for " + endpoint)
try: try:
response = get(url, headers=headers) response = get(HASS_URL + endpoint, headers=headers)
return response.json() return response.json()
except: except:
return {} return {}
def __post(self, endpoint, d) -> bool: def postReq(endpoint, d) -> bool:
gc.collect() collect()
url = self.base_url + endpoint
headers = { headers = {
"Authorization": "Bearer " + self.access_token, "Authorization": "Bearer " + TOKEN,
"content-type": "application/json" "content-type": "application/json"
} }
print("Starting post request to " + endpoint) print("Starting post request to " + endpoint)
try: try:
response = post(url, headers=headers, json=d) response = post(HASS_URL + endpoint, headers=headers, json=d)
return response.status_code == 200 return response.status_code == 200
except: except:
return False return False
def getLightData(self, entity_id: str) -> dict: def getLightData(entity_id: str) -> dict:
# TODO: error handling if can't access hass (e.g. no connection, invalid token) # TODO: error handling if can't access hass (e.g. no connection, invalid token)
response = self.__request("/api/states/" + entity_id) response = getReq("/api/states/" + entity_id)
on = "state" in response and response["state"] == "on" on = "state" in response and response["state"] == "on"
return { return {
"on": on, "on": on,
@@ -46,8 +40,13 @@ class Api:
"brightness": response["attributes"]["brightness"] if on else 0.0 "brightness": response["attributes"]["brightness"] if on else 0.0
} }
def getMediaPlayerData(self, entity_id: str) -> dict: def getMediaPlayerData(entity_id: str) -> dict:
response = self.__request("/api/states/" + entity_id) response = getReq("/api/states/" + entity_id)
if (not "state" in response or not "attributes" in response):
response = {
"state": "",
"attributes": {}
}
e = "state" in response and "attributes" in response e = "state" in response and "attributes" in response
if ("media_position" in (dict)(response["attributes"])): if ("media_position" in (dict)(response["attributes"])):
p = response["attributes"]["media_position"] p = response["attributes"]["media_position"]
@@ -55,8 +54,8 @@ class Api:
p = 0 p = 0
if ("media_position_updated_at" in (dict)(response["attributes"])): if ("media_position_updated_at" in (dict)(response["attributes"])):
dts = response["attributes"]["media_position_updated_at"] dts = response["attributes"]["media_position_updated_at"]
t = time.mktime((int(dts[0:4]), int(dts[5:7]), int(dts[8:10]), int(dts[11:13]) + int(dts[27:29]), int(dts[14:16]) + int(dts[30:31]), int(dts[17:19]), 0, 0)) t = mktime((int(dts[0:4]), int(dts[5:7]), int(dts[8:10]), int(dts[11:13]) + int(dts[27:29]), int(dts[14:16]) + int(dts[30:31]), int(dts[17:19]), 0, 0))
p += time.mktime(time.localtime()) - t p += mktime(localtime()) - t
return { return {
"playing": response["state"] in ["on", "playing", "buffering"], "playing": response["state"] in ["on", "playing", "buffering"],
"shuffle": response["attributes"]["shuffle"] if e and "shuffle" in response["attributes"] else False, "shuffle": response["attributes"]["shuffle"] if e and "shuffle" in response["attributes"] else False,
@@ -70,23 +69,26 @@ class Api:
"media_album_name": response["attributes"]["media_album_name"] if e and "media_album_name" in (dict)(response["attributes"]) else "" "media_album_name": response["attributes"]["media_album_name"] if e and "media_album_name" in (dict)(response["attributes"]) else ""
} }
def changeVolume(self, entity_id: str, up: bool = True) -> None: def changeVolume(entity_id: str, up: bool = True) -> None:
if (up): dir = "up" if (up): dir = "up"
else: dir = "down" else: dir = "down"
self.__post(f"/api/services/media_player/volume_{dir}", {"entity_id": entity_id}) postReq(f"/api/services/media_player/volume_{dir}", {"entity_id": entity_id})
self.__post(f"/api/services/media_player/volume_{dir}", {"entity_id": entity_id}) postReq(f"/api/services/media_player/volume_{dir}", {"entity_id": entity_id})
def nextTrack(self, entity_id: str) -> None: def setVolume(entity_id: str, v: float) -> None:
self.__post("/api/services/media_player/media_next_track", {"entity_id": entity_id}) postReq(f"/api/services/media_player/volume_set", {"entity_id": entity_id, "volume_level": v})
def prevTrack(self, entity_id: str) -> None: def nextTrack(entity_id: str) -> None:
self.__post("/api/services/media_player/media_previous_track", {"entity_id": entity_id}) postReq("/api/services/media_player/media_next_track", {"entity_id": entity_id})
def playPause(self, entity_id: str) -> None: def prevTrack(entity_id: str) -> None:
self.__post("/api/services/media_player/media_play_pause", {"entity_id": entity_id}) postReq("/api/services/media_player/media_previous_track", {"entity_id": entity_id})
def toggleLight(self, entity_id: str) -> None: def playPause(entity_id: str) -> None:
self.__post("/api/services/light/toggle", {"entity_id": entity_id}) postReq("/api/services/media_player/media_play_pause", {"entity_id": entity_id})
def setBrightness(self, entity_id: str, v: int) -> None: def toggleLight(entity_id: str) -> None:
self.__post("/api/services/light/turn_on", {"entity_id": entity_id, "brightness": v}) postReq("/api/services/light/toggle", {"entity_id": entity_id})
def setBrightness(entity_id: str, v: int) -> None:
postReq("/api/services/light/turn_on", {"entity_id": entity_id, "brightness": v})

68
app.py
View File

@@ -1,14 +1,12 @@
from api import Api from machine import reset
import machine from network import WLAN, STA_IF
import network from time import sleep, localtime
from time import sleep
from lcd import LCD from lcd import LCD
from font import Font from font import cntr_st
import gc from gc import collect, mem_free
from env import * from env import HOSTNAME, SSID, WIFI_PASSWORD, SCREENS
import _thread from _thread import start_new_thread
import ntptime from ntptime import settime
import time
from screens import * from screens import *
class App: class App:
@@ -18,10 +16,9 @@ class App:
self.scr_n = -1 self.scr_n = -1
else: self.scr_n = 0 else: self.scr_n = 0
self.lcd = LCD() self.lcd = LCD()
self.api = Api(HASS_URL, TOKEN)
def __connect(self) -> int: def __connect(self) -> int:
wlan = network.WLAN(network.STA_IF) wlan = WLAN(STA_IF)
wlan.active(True) wlan.active(True)
wlan.config(hostname=HOSTNAME) wlan.config(hostname=HOSTNAME)
wlan.connect(SSID, WIFI_PASSWORD) wlan.connect(SSID, WIFI_PASSWORD)
@@ -33,16 +30,15 @@ class App:
return self.ip return self.ip
def __boot(self) -> None: def __boot(self) -> None:
gc.collect() collect()
print("Booting") print("Booting")
self.lcd.fill(self.lcd.black) self.lcd.fill(0x0000)
Font.cntr_st(self.lcd, self.lcd.width, "Booting...", 120, 2, 150, 150, 150) cntr_st(self.lcd, self.lcd.width, "Booting...", 120, 2, 150, 150, 150)
self.lcd.show() self.lcd.show()
self.__connect() self.__connect()
ntptime.host = "1.europe.pool.ntp.org"
try: try:
ntptime.settime() settime()
print("Local time after synchronization: %s" %str(time.localtime())) print("Local time after synchronization: %s" %str(localtime()))
except: except:
pass pass
@@ -109,7 +105,7 @@ class App:
print(f"Screen change: {c}") print(f"Screen change: {c}")
return c return c
def handleButtons(self): def handleButtons(self) -> bool:
up = self.lcd.up["v"] up = self.lcd.up["v"]
down = self.lcd.down["v"] down = self.lcd.down["v"]
left = self.lcd.left["v"] left = self.lcd.left["v"]
@@ -119,27 +115,21 @@ class App:
keyB = self.lcd.keyB["v"] keyB = self.lcd.keyB["v"]
keyX = self.lcd.keyX["v"] keyX = self.lcd.keyX["v"]
keyY = self.lcd.keyY["v"] keyY = self.lcd.keyY["v"]
# self.lcd.up["v"] = False
# self.lcd.down["v"] = False
# self.lcd.left["v"] = False
# self.lcd.right["v"] = False
# self.lcd.ctrl["v"] = False
# self.lcd.keyA["v"] = False
# self.lcd.keyB["v"] = False
# self.lcd.keyX["v"] = False
# self.lcd.keyY["v"] = False
self.__resetButtonStatuses() self.__resetButtonStatuses()
if (ctrl): if (ctrl):
machine.reset() reset()
self.s.handleButtons(up, down, left, right, keyA, keyB, keyX, keyY, ctrl) return self.s.handleButtons(up, down, left, right, keyA, keyB, keyX, keyY, ctrl)
def __manageScreen(self) -> None: def __manageScreen(self) -> None:
started = False started = False
while (True): while (True):
gc.collect() print("Mem free before and after collecting:")
if (time.localtime()[3] == 0): print(mem_free())
collect()
print(mem_free())
if (localtime()[3] == 0):
try: try:
ntptime.settime() settime()
except: except:
pass pass
changed = not started or self.__changeScreen() changed = not started or self.__changeScreen()
@@ -147,13 +137,13 @@ class App:
# if the screen has changed, redraw the whole screen # if the screen has changed, redraw the whole screen
if (changed): if (changed):
self.__resetButtonStatuses() self.__resetButtonStatuses()
gc.collect() collect()
if (SCREENS[self.scr_n]["type"] == 0): if (SCREENS[self.scr_n]["type"] == 0):
self.s = LightsScreen(self.api, SCREENS[self.scr_n]["name"], SCREENS[self.scr_n]["entities"]) self.s = LightsScreen(SCREENS[self.scr_n]["name"], SCREENS[self.scr_n]["entities"])
elif (SCREENS[self.scr_n]["type"] == 1): elif (SCREENS[self.scr_n]["type"] == 1):
self.s = MediaScreen(self.api, SCREENS[self.scr_n]["name"], SCREENS[self.scr_n]["entity"]) self.s = MediaScreen(SCREENS[self.scr_n]["name"], SCREENS[self.scr_n]["entity"])
else: else:
self.s = UnknownScreen(self.api, -1, "Unknown") self.s = UnknownScreen("Unknown")
self.s.display(self.lcd) self.s.display(self.lcd)
# otherwise minimise the number of pixels being changed # otherwise minimise the number of pixels being changed
else: else:
@@ -165,7 +155,7 @@ class App:
if (self.scr_n == None): return if (self.scr_n == None): return
self.__boot() self.__boot()
try: try:
_thread.start_new_thread(self.__manageButtons, ()) start_new_thread(self.__manageButtons, ())
self.__manageScreen() self.__manageScreen()
except KeyboardInterrupt: except KeyboardInterrupt:
machine.reset() reset()

23
font.py
View File

@@ -1,4 +1,4 @@
from utils import Utils from utils import colour
# ===========Start of FONTS Section========================= # ===========Start of FONTS Section=========================
# Standard ASCII 5x8 font # Standard ASCII 5x8 font
@@ -264,10 +264,8 @@ FONT = bytes([
0x00, 0x00, 0x00, 0x00, 0x00 # 255 also a <space> 0x00, 0x00, 0x00, 0x00, 0x00 # 255 also a <space>
]) ])
class Font: def character(lcd,asc,xt,yt,sz,r,g,b): # Single character sz is size: 1 or 2
@staticmethod cc = colour(r,g,b)
def character(lcd,asc,xt,yt,sz,r,g,b): # Single character sz is size: 1 or 2
cc = Utils.colour(r,g,b)
code = asc * 5 # 5 bytes per character code = asc * 5 # 5 bytes per character
for ii in range(5): for ii in range(5):
line = FONT[code + ii] line = FONT[code + ii]
@@ -285,30 +283,27 @@ class Font:
lcd.pixel(ii*sz+xt+2,yy*sz+yt,cc) lcd.pixel(ii*sz+xt+2,yy*sz+yt,cc)
lcd.pixel(ii*sz+xt+2,yy*sz+yt+1,cc) lcd.pixel(ii*sz+xt+2,yy*sz+yt+1,cc)
@staticmethod def prnt_st(lcd,asci,xx,yy,sz,r,g,b): # Text string
def prnt_st(lcd,asci,xx,yy,sz,r,g,b): # Text string
if sz == 1: move = 6 if sz == 1: move = 6
if sz == 2: move = 11 if sz == 2: move = 11
if sz == 3: move = 17 if sz == 3: move = 17
for letter in(asci): for letter in(asci):
asci = ord(letter) asci = ord(letter)
Font.character(lcd,asci,xx,yy,sz,r,g,b) character(lcd,asci,xx,yy,sz,r,g,b)
xx = xx + move xx = xx + move
@staticmethod def cntr_st(lcd,width,txt,y,size,r,g,b,o=0): # Centres text on line y, skipping first o pixels
def cntr_st(lcd,width,txt,y,size,r,g,b,o=0): # Centres text on line y, skipping first o pixels
if size == 1: w = 6 if size == 1: w = 6
if size == 2: w = 11 if size == 2: w = 11
if size == 3: w = 17 if size == 3: w = 17
gap = (width - len(txt) * w)//2 + o gap = (width - len(txt) * w)//2 + o
Font.prnt_st(lcd,txt,gap,y,size,r,g,b) prnt_st(lcd,txt,gap,y,size,r,g,b)
@staticmethod def rght_st(lcd,asci,xx,yy,sz,r,g,b):
def rght_st(lcd,asci,xx,yy,sz,r,g,b):
if sz == 1: w = 6 if sz == 1: w = 6
if sz == 2: w = 11 if sz == 2: w = 11
if sz == 3: w = 17 if sz == 3: w = 17
xo = xx - len(asci) * w xo = xx - len(asci) * w
Font.prnt_st(lcd,asci,xo,yy,sz,r,g,b) prnt_st(lcd,asci,xo,yy,sz,r,g,b)
# =========== End of font support routines =========== # =========== End of font support routines ===========

17
lcd.py
View File

@@ -1,7 +1,5 @@
from machine import Pin,SPI,PWM from machine import Pin,SPI,PWM
from font import Font from framebuf import FrameBuffer, RGB565
from utils import Utils
import framebuf
BL = 13 BL = 13
DC = 8 DC = 8
@@ -11,7 +9,7 @@ SCK = 10
CS = 9 CS = 9
# LCD driver # LCD driver
class LCD(framebuf.FrameBuffer): class LCD(FrameBuffer):
def __init__(self): def __init__(self):
pwm = PWM(Pin(BL)) pwm = PWM(Pin(BL))
pwm.freq(1000) pwm.freq(1000)
@@ -30,15 +28,9 @@ class LCD(framebuf.FrameBuffer):
self.dc = Pin(DC,Pin.OUT) self.dc = Pin(DC,Pin.OUT)
self.dc(1) self.dc(1)
self.buffer = bytearray(self.height * self.width * 2) self.buffer = bytearray(self.height * self.width * 2)
super().__init__(self.buffer, self.width, self.height, framebuf.RGB565) super().__init__(self.buffer, self.width, self.height, RGB565)
self.init_display() self.init_display()
self.red = 0x07E0
self.green = 0x001f
self.blue = 0xf800
self.white = 0xffff
self.black = 0x0000
self.keyA = { self.keyA = {
"v": False, # value "v": False, # value
"p": Pin(15,Pin.IN,Pin.PULL_UP) # pin - normally 1 but 0 if pressed "p": Pin(15,Pin.IN,Pin.PULL_UP) # pin - normally 1 but 0 if pressed
@@ -194,6 +186,3 @@ class LCD(framebuf.FrameBuffer):
self.spi.write(self.buffer) self.spi.write(self.buffer)
self.cs(1) self.cs(1)
# END OF DRIVER # END OF DRIVER
def clear(self, c):
self.fill(c)

View File

@@ -1,17 +1,17 @@
from font import Font from font import cntr_st, rght_st
from utils import Utils from utils import colour
import bmp_file_reader as bmpr from bmp_file_reader import BMPFileReader
from api import getMediaPlayerData, getLightData, playPause, nextTrack, prevTrack, changeVolume, setVolume, toggleLight, setBrightness
class Screen(): class Screen():
def __init__(self, api, n: str) -> None: def __init__(self, n: str) -> None:
self.api = api
self.name = n self.name = n
self.d = {} self.d = {}
self.prev = {} self.prev = {}
def display(self, lcd) -> None: def display(self, lcd) -> None:
lcd.fill(lcd.black) lcd.fill(0x0000)
Font.cntr_st(lcd, lcd.width, self.name, 20, 2, 255, 255, 255) cntr_st(lcd, lcd.width, self.name, 20, 2, 255, 255, 255)
def update(self, lcd) -> None: def update(self, lcd) -> None:
pass pass
@@ -20,15 +20,16 @@ class Screen():
return False return False
def _updateData(self) -> dict: def _updateData(self) -> dict:
self.prev = self.d.copy()
return {} return {}
def _invalidConfig(self, lcd) -> None: def _invalidConfig(self, lcd) -> None:
Font.cntr_st(lcd, lcd.width, "Invalid config", lcd.height//2, 2, 255, 255, 255) cntr_st(lcd, lcd.width, "Invalid config", lcd.height//2, 2, 255, 255, 255)
lcd.show() lcd.show()
class MediaScreen(Screen): class MediaScreen(Screen):
def __init__(self, api, n: str, e: str) -> None: def __init__(self, n: str, e: str) -> None:
super().__init__(api, n) super().__init__(n)
self.e = e self.e = e
self.valid = e != None and e != "" self.valid = e != None and e != ""
@@ -43,20 +44,20 @@ class MediaScreen(Screen):
if (self.d["media_duration"] != None): if (self.d["media_duration"] != None):
mins = self.d["media_duration"] // 60 mins = self.d["media_duration"] // 60
secs = self.d["media_duration"] % 60 secs = self.d["media_duration"] % 60
Font.rght_st(lcd, f"{mins}:{secs}", lcd.width, lcd.height - 16, 1, 180, 180, 180) rght_st(lcd, f"{mins}:{secs}", lcd.width, lcd.height - 16, 1, 180, 180, 180)
Font.cntr_st(lcd, lcd.width, self.d["media_title"], lcd.height - 72, 3, 255, 255, 255) cntr_st(lcd, lcd.width, self.d["media_title"], lcd.height - 72, 3, 255, 255, 255)
Font.cntr_st(lcd, lcd.width, self.d["media_artist"], lcd.height - 96, 2, 255, 255, 255) cntr_st(lcd, lcd.width, self.d["media_artist"], lcd.height - 96, 2, 255, 255, 255)
lcd.show() lcd.show()
def __updateMediaPositionBar(self, lcd, p: int, d: int): def __updateMediaPositionBar(self, lcd, p: int, d: int):
if (d > 0): if (d > 0):
for x in range (0, (lcd.width * p)//d): for x in range (0, (lcd.width * p)//d):
lcd.pixel(x, lcd.height - 5, lcd.white) lcd.pixel(x, lcd.height - 5, 0xffff)
lcd.pixel(x, lcd.height - 4, lcd.white) lcd.pixel(x, lcd.height - 4, 0xffff)
lcd.pixel(x, lcd.height - 3, lcd.white) lcd.pixel(x, lcd.height - 3, 0xffff)
lcd.pixel(x, lcd.height - 2, lcd.white) lcd.pixel(x, lcd.height - 2, 0xffff)
lcd.pixel(x, lcd.height - 1, lcd.white) lcd.pixel(x, lcd.height - 1, 0xffff)
lcd.pixel(x, lcd.height, lcd.white) lcd.pixel(x, lcd.height, 0xffff)
def update(self, lcd): def update(self, lcd):
if (not self.valid): if (not self.valid):
@@ -73,29 +74,34 @@ class MediaScreen(Screen):
self.display(lcd) self.display(lcd)
def _updateData(self) -> dict: def _updateData(self) -> dict:
self.prev = self.d super()._updateData()
self.d = self.api.getMediaPlayerData(self.e) self.d = getMediaPlayerData(self.e)
return self.d return self.d
def handleButtons(self, up: bool, down: bool, left: bool, right: bool, keyA: bool, keyB: bool, keyX: bool, keyY: bool, ctrl: bool) -> bool: def handleButtons(self, up: bool, down: bool, left: bool, right: bool, keyA: bool, keyB: bool, keyX: bool, keyY: bool, ctrl: bool) -> bool:
a = False a = False
if (up): v = self.d["volume_level"] if "volume_level" in self.d else None
self.api.changeVolume(self.e) if (v != None and up and v < 1):
vn = min(1.0, v + 0.08)
setVolume(self.e, vn)
self.d["volume_level"] = vn
a = True a = True
elif (down): elif (v != None and down and v > 0.01):
self.api.changeVolume(self.e, False) vn = max(0.01, v - 0.08)
setVolume(self.e, vn)
self.d["volume_level"] = vn
a = True a = True
if (keyX): if (keyX):
self.api.nextTrack(self.e) nextTrack(self.e)
elif (keyY): elif (keyY):
self.api.prevTrack(self.e) prevTrack(self.e)
if (keyA): if (keyA):
self.api.playPause(self.e) playPause(self.e)
return a return a
class LightsScreen(Screen): class LightsScreen(Screen):
def __init__(self, api, n: str, es: list) -> None: def __init__(self, n: str, es: list) -> None:
super().__init__(api, n) super().__init__(n)
self.es = es self.es = es
self.valid = es != None and len(es) != 0 self.valid = es != None and len(es) != 0
@@ -115,9 +121,9 @@ class LightsScreen(Screen):
def __displayLightEntity(self, lcd, i: int, w: int, h: int, xo: int, yo: int, n: str, d) -> None: def __displayLightEntity(self, lcd, i: int, w: int, h: int, xo: int, yo: int, n: str, d) -> None:
# if the light is turned on, display the filled-in lightbulb icon in the colour of the light, centrally in the light's grid square # if the light is turned on, display the filled-in lightbulb icon in the colour of the light, centrally in the light's grid square
if (d["on"]): if (d["on"]):
color = Utils.colour(d["rgb_color"][0], d["rgb_color"][1], d["rgb_color"][2]) color = colour(d["rgb_color"][0], d["rgb_color"][1], d["rgb_color"][2])
with open("images/lightbulb-on.bmp", "rb") as file_handle: with open("images/lightbulb-on.bmp", "rb") as file_handle:
reader = bmpr.BMPFileReader(file_handle) reader = BMPFileReader(file_handle)
img_height = reader.get_height() img_height = reader.get_height()
x_offset = w//2 + xo - reader.get_width()//2 x_offset = w//2 + xo - reader.get_width()//2
y_offset = h//2 + yo - reader.get_height()//2 - 4 y_offset = h//2 + yo - reader.get_height()//2 - 4
@@ -128,12 +134,14 @@ class LightsScreen(Screen):
g = d["rgb_color"][1] if (color.green) != 0 else 0 g = d["rgb_color"][1] if (color.green) != 0 else 0
b = d["rgb_color"][2] if (color.blue) != 0 else 0 b = d["rgb_color"][2] if (color.blue) != 0 else 0
if (color.red != 0 or color.green != 0 or color.blue != 0): if (color.red != 0 or color.green != 0 or color.blue != 0):
lcd.pixel(col_i + x_offset, row_i + y_offset, Utils.colour(r,g,b)) lcd.pixel(col_i + x_offset, row_i + y_offset, colour(r,g,b))
# otherwise display the outline lightbulb icon in grey, centrally in the light's grid square # otherwise display the outline lightbulb icon in grey, centrally in the light's grid square
else: else:
color = Utils.colour(80, 80, 80) color = colour(80, 80, 80)
print("Drawing light off")
with open("images/lightbulb-off.bmp", "rb") as file_handle: with open("images/lightbulb-off.bmp", "rb") as file_handle:
reader = bmpr.BMPFileReader(file_handle) reader = BMPFileReader(file_handle)
print("Drawing light off - loaded file")
img_height = reader.get_height() img_height = reader.get_height()
x_offset = w//2 + xo - reader.get_width()//2 x_offset = w//2 + xo - reader.get_width()//2
y_offset = h//2 + yo - reader.get_height()//2 - 4 y_offset = h//2 + yo - reader.get_height()//2 - 4
@@ -141,11 +149,11 @@ class LightsScreen(Screen):
row = reader.get_row(row_i) row = reader.get_row(row_i)
for col_i, color in enumerate(row): for col_i, color in enumerate(row):
if (color.red != 0 or color.green != 0 or color.blue != 0): if (color.red != 0 or color.green != 0 or color.blue != 0):
lcd.pixel(col_i + x_offset, row_i + y_offset, Utils.colour(color.red, color.green, color.blue)) lcd.pixel(col_i + x_offset, row_i + y_offset, colour(color.red, color.green, color.blue))
else: else:
lcd.pixel(col_i + x_offset, row_i + y_offset, Utils.colour(0,0,0)) lcd.pixel(col_i + x_offset, row_i + y_offset, colour(0,0,0))
# display the name of the light 8px below the lightbulb icon # display the name of the light 8px below the lightbulb icon
Font.cntr_st(lcd, w, n, y_offset + img_height + 8, 2, 220, 220, 220, xo) cntr_st(lcd, w, n, y_offset + img_height + 8, 2, 220, 220, 220, xo)
def update(self, lcd): def update(self, lcd):
if (not self.valid): if (not self.valid):
@@ -153,6 +161,7 @@ class LightsScreen(Screen):
self._invalidConfig(lcd) self._invalidConfig(lcd)
return return
self._updateData() self._updateData()
print("Updating lights")
# for each light to be displayed # for each light to be displayed
for i in range(0, len(self.d)): for i in range(0, len(self.d)):
# if its settings have changed, re-draw them without clearing the display # if its settings have changed, re-draw them without clearing the display
@@ -161,9 +170,9 @@ class LightsScreen(Screen):
lcd.show() lcd.show()
def _updateData(self) -> dict: def _updateData(self) -> dict:
self.prev = self.d super()._updateData()
for i in range(0, min(len(self.es), 4)): for i in range(0, min(len(self.es), 4)):
self.d[i] = self.api.getLightData(self.es[i]["id"]) self.d[i] = getLightData(self.es[i]["id"])
return self.d return self.d
def handleButtons(self, up: bool, down: bool, left: bool, right: bool, keyA: bool, keyB: bool, keyX: bool, keyY: bool, ctrl: bool) -> bool: def handleButtons(self, up: bool, down: bool, left: bool, right: bool, keyA: bool, keyB: bool, keyX: bool, keyY: bool, ctrl: bool) -> bool:
@@ -173,21 +182,21 @@ class LightsScreen(Screen):
for i in range(0, e): for i in range(0, e):
# if button for light clicked, toggle the light # if button for light clicked, toggle the light
if (b[i]): if (b[i]):
self.api.toggleLight(self.es[i]["id"]) toggleLight(self.es[i]["id"])
a = True a = True
# if up/down clicked, adjust brightness for all lights that are turned on # if up/down clicked, adjust brightness for all lights that are turned on
pcfg = i in self.prev and "on" in self.prev[i] and self.prev[i]["on"] and "brightness" in self.prev[i] pcfg = i in self.prev and "on" in self.prev[i] and self.prev[i]["on"] and "brightness" in self.prev[i]
if (up and pcfg): if (up and pcfg):
self.api.setBrightness(self.es[i]["id"], min(255, self.prev[i]["brightness"] + 35)) setBrightness(self.es[i]["id"], min(255, self.prev[i]["brightness"] + 35))
a = True a = True
elif (down and pcfg): elif (down and pcfg):
self.api.setBrightness(self.es[i]["id"], max(1, self.prev[i]["brightness"] - 35)) setBrightness(self.es[i]["id"], max(1, self.prev[i]["brightness"] - 35))
a = True a = True
return a return a
class UnknownScreen(Screen): class UnknownScreen(Screen):
def __init__(self, api, t: int, n: str) -> None: def __init__(self, n: str) -> None:
super().__init__(api, t, n) super().__init__(n)
def display(self, lcd) -> None: def display(self, lcd) -> None:
super().display(lcd) super().display(lcd)

View File

@@ -1,5 +1,3 @@
class Utils: # method from Tony Goodhew 21st April 2022, for thepihut.com
# method from Tony Goodhew 21st April 2022, for thepihut.com def colour(R,G,B): # Convert RGB888 to RGB565
@staticmethod
def colour(R,G,B): # Convert RGB888 to RGB565
return (((G&0b00011100)<<3) +((B&0b11111000)>>3)<<8) + (R&0b11111000)+((G&0b11100000)>>5) return (((G&0b00011100)<<3) +((B&0b11111000)>>3)<<8) + (R&0b11111000)+((G&0b11100000)>>5)