Some improvements and fixes, and test file

This commit is contained in:
Maximilian Giller 2025-09-22 05:53:41 +02:00
parent 8323c7a4be
commit 4f346f7534
3 changed files with 203 additions and 11 deletions

View file

@ -52,7 +52,7 @@ class Engine:
) )
del self.fixtures[id] del self.fixtures[id]
def next(self, step_size: Decimal): def step(self, step_size: Decimal):
"""Calculate light values for next step in time. Step size in seconds.""" """Calculate light values for next step in time. Step size in seconds."""
if not self.is_running: if not self.is_running:
raise BaseException("Cannot render next step, already finished all tracks.") raise BaseException("Cannot render next step, already finished all tracks.")
@ -69,10 +69,10 @@ class Engine:
# Move track forward # Move track forward
t.step(step_size) t.step(step_size)
if t.current is None: if t.current is None:
raise BaseException(f"Track has no current keyframe.") continue # Track is not ready yet
# Interpolate and update value # Interpolate and update value
interpolated_value = self.get_current_value(t) interpolated_value = self._get_current_value(t)
# Update values of relevant fixtures # Update values of relevant fixtures
for f in self.fixtures.values(): for f in self.fixtures.values():
@ -90,7 +90,7 @@ class Engine:
api = self.apis[f.fixture.api] api = self.apis[f.fixture.api]
api.update_fixture(f) api.update_fixture(f)
def get_current_value(self, track: EngineTrack) -> KeyframeValue: def _get_current_value(self, track: EngineTrack) -> KeyframeValue:
"""Interpolate between current keyframes of track and return the interpolated value.""" """Interpolate between current keyframes of track and return the interpolated value."""
current = track.current current = track.current
if current is None: if current is None:
@ -106,11 +106,11 @@ class Engine:
current.delta.copy_abs() + next.delta.copy_abs() current.delta.copy_abs() + next.delta.copy_abs()
) )
return self.interpolate_value( return self._interpolate_value(
current.frame.value, next.frame.value, t, current.frame.interpolation current.frame.value, next.frame.value, t, current.frame.interpolation
) )
def interpolate_value( def _interpolate_value(
self, self,
start: KeyframeValue, start: KeyframeValue,
end: KeyframeValue, end: KeyframeValue,
@ -150,9 +150,9 @@ class Engine:
case Interpolation.SMOOTHSTEP: case Interpolation.SMOOTHSTEP:
f = t * t * (3 - 2 * t) f = t * t * (3 - 2 * t)
case Interpolation.BOUNCE: case Interpolation.BOUNCE:
f = self.bounce_out(float(t)) f = self._bounce_out(float(t))
case Interpolation.ELASTIC: case Interpolation.ELASTIC:
f = self.elastic_out(t) f = self._elastic_out(t)
case _: case _:
logging.warning( logging.warning(
f"Unhandled interpolation type [{interpolation}]. Using STEP." f"Unhandled interpolation type [{interpolation}]. Using STEP."
@ -161,7 +161,7 @@ class Engine:
raise BaseException("Unreachable code reached.") raise BaseException("Unreachable code reached.")
def bounce_out(self, t: float) -> float: def _bounce_out(self, t: float) -> float:
"""CHATGPT. Bounce easing function (out).""" """CHATGPT. Bounce easing function (out)."""
n1, d1 = 7.5625, 2.75 n1, d1 = 7.5625, 2.75
if t < 1 / d1: if t < 1 / d1:
@ -176,7 +176,7 @@ class Engine:
t -= 2.625 / d1 t -= 2.625 / d1
return n1 * t * t + 0.984375 return n1 * t * t + 0.984375
def elastic_out(self, t: Decimal) -> float: def _elastic_out(self, t: Decimal) -> float:
"""CHATGPT. Elastic easing function (out).""" """CHATGPT. Elastic easing function (out)."""
c4 = (2 * math.pi) / 3 c4 = (2 * math.pi) / 3
if t == 0: if t == 0:

View file

@ -26,7 +26,7 @@ class HueApi(FlickrApiInterface):
command["sat"] = fixture.brightness * 255 command["sat"] = fixture.brightness * 255
command["hue"] = fixture.hue * 65535 command["hue"] = fixture.hue * 65535
if fixture.fixture.capabilities in [Capabilities.TEMPERATURE]: if fixture.fixture.capabilities in [Capabilities.TEMPERATURE]:
command["ct"] = fixture.temperature command["ct"] = fixture.temperature / 10
# Apply update # Apply update
self.bridge.set_light(fixture.fixture.api_id, command) self.bridge.set_light(fixture.fixture.api_id, command)

192
src/test.ipynb Normal file
View file

@ -0,0 +1,192 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 13,
"id": "1177b81b",
"metadata": {},
"outputs": [],
"source": [
"from phue import Bridge, Light\n",
"\n",
"ip = \"192.168.178.85\"\n",
"b = Bridge(ip)\n",
"b.connect()\n",
"# b.get_api()\n",
"\n",
"l: Light = b.get_light_objects(\"id\")[1]"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "1171ac4d",
"metadata": {},
"outputs": [],
"source": [
"l.brightness = 1"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "03e74b50",
"metadata": {},
"outputs": [],
"source": [
"from models import Flickr, Interpolation, Space, Fixture, Capabilities, SpaceVector, Sequence, Track, Parameter, Keyframe\n",
"from decimal import Decimal\n",
"\n",
"f = Flickr(\n",
" [\n",
" Space(\n",
" \"max\",\n",
" \"Max' Room\",\n",
" [\n",
" Fixture(\n",
" \"stehlampe\",\n",
" \"Stehlampe\",\n",
" \"hue\",\n",
" \"1\",\n",
" Capabilities.COLOR,\n",
" SpaceVector(),\n",
" SpaceVector(),\n",
" SpaceVector(),\n",
" 0.5,\n",
" )\n",
" ],\n",
" )\n",
" ],\n",
" [\n",
" Sequence(\n",
" \"test\",\n",
" \"test\",\n",
" [\n",
" Track(\n",
" \"switch\",\n",
" \"Switch\",\n",
" Parameter.ON,\n",
" [\"1\"],\n",
" [Keyframe(Decimal.from_float(0), True)],\n",
" ),\n",
" Track(\n",
" \"bri\",\n",
" \"Brightness\",\n",
" Parameter.BRIGHTNESS,\n",
" [\"1\"],\n",
" [\n",
" Keyframe(Decimal.from_float(0), 1),\n",
" Keyframe(Decimal.from_float(1), 0),\n",
" Keyframe(Decimal.from_float(2), 0, Interpolation.LINEAR),\n",
" Keyframe(Decimal.from_float(3), 1, Interpolation.LINEAR),\n",
" Keyframe(Decimal.from_float(4), 0, Interpolation.EASE_IN_OUT),\n",
" Keyframe(Decimal.from_float(5), 1, Interpolation.EASE_IN_OUT),\n",
" Keyframe(Decimal.from_float(6), 0, Interpolation.EXPONENTIAL),\n",
" Keyframe(Decimal.from_float(7), 1, Interpolation.EXPONENTIAL),\n",
" Keyframe(Decimal.from_float(8), 0),\n",
" Keyframe(Decimal.from_float(9), 0.5),\n",
" ],\n",
" ),\n",
" Track(\n",
" \"hue\",\n",
" \"Hue\",\n",
" Parameter.HUE,\n",
" [\"1\"],\n",
" [\n",
" Keyframe(Decimal.from_float(0), 0),\n",
" Keyframe(Decimal.from_float(9), 0, Interpolation.LINEAR),\n",
" Keyframe(Decimal.from_float(15), 1),\n",
" ],\n",
" ),\n",
" Track(\n",
" \"sat\",\n",
" \"Sat\",\n",
" Parameter.SATURATION,\n",
" [\"1\"],\n",
" [\n",
" Keyframe(Decimal.from_float(0), 0),\n",
" Keyframe(Decimal.from_float(9), 1),\n",
" ],\n",
" )\n",
" ],\n",
" )\n",
" ],\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "c51314e4",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING:phue:ERROR: resource, /lights/False/state, not available for light 1\n",
"WARNING:phue:ERROR: resource, /lights/False/state, not available for light 1\n",
"WARNING:phue:ERROR: resource, /lights/False/state, not available for light 1\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Done.\n"
]
}
],
"source": [
"from engine import Engine\n",
"from hue import HueApi\n",
"from models import FlickrApiInterface\n",
"from time import time, sleep\n",
"\n",
"\n",
"apis: dict[str, FlickrApiInterface] = {\n",
" \"hue\": HueApi(ip)\n",
"}\n",
"\n",
"engine = Engine(f, apis)\n",
"\n",
"engine.load_sequence(\"test\")\n",
"\n",
"previous_time = None\n",
"while engine.is_running:\n",
" curr_time = time()\n",
" if previous_time is None:\n",
" previous_time = curr_time\n",
" step = curr_time - previous_time\n",
" previous_time = curr_time\n",
" \n",
" engine.step(Decimal.from_float(step))\n",
" engine.propagate()\n",
" sleep(0.2)\n",
"\n",
"print(\"Done.\") \n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}