From 4f346f753422aae592b9c362bed65a2449abc9dd Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Mon, 22 Sep 2025 05:53:41 +0200 Subject: [PATCH] Some improvements and fixes, and test file --- src/engine.py | 20 +++--- src/hue.py | 2 +- src/test.ipynb | 192 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 11 deletions(-) create mode 100644 src/test.ipynb diff --git a/src/engine.py b/src/engine.py index 32c3800..d08b2aa 100644 --- a/src/engine.py +++ b/src/engine.py @@ -52,7 +52,7 @@ class Engine: ) 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.""" if not self.is_running: raise BaseException("Cannot render next step, already finished all tracks.") @@ -69,10 +69,10 @@ class Engine: # Move track forward t.step(step_size) if t.current is None: - raise BaseException(f"Track has no current keyframe.") + continue # Track is not ready yet # Interpolate and update value - interpolated_value = self.get_current_value(t) + interpolated_value = self._get_current_value(t) # Update values of relevant fixtures for f in self.fixtures.values(): @@ -90,7 +90,7 @@ class Engine: api = self.apis[f.fixture.api] 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.""" current = track.current if current is None: @@ -106,11 +106,11 @@ class Engine: 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 ) - def interpolate_value( + def _interpolate_value( self, start: KeyframeValue, end: KeyframeValue, @@ -150,9 +150,9 @@ class Engine: case Interpolation.SMOOTHSTEP: f = t * t * (3 - 2 * t) case Interpolation.BOUNCE: - f = self.bounce_out(float(t)) + f = self._bounce_out(float(t)) case Interpolation.ELASTIC: - f = self.elastic_out(t) + f = self._elastic_out(t) case _: logging.warning( f"Unhandled interpolation type [{interpolation}]. Using STEP." @@ -161,7 +161,7 @@ class Engine: raise BaseException("Unreachable code reached.") - def bounce_out(self, t: float) -> float: + def _bounce_out(self, t: float) -> float: """CHATGPT. Bounce easing function (out).""" n1, d1 = 7.5625, 2.75 if t < 1 / d1: @@ -176,7 +176,7 @@ class Engine: t -= 2.625 / d1 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).""" c4 = (2 * math.pi) / 3 if t == 0: diff --git a/src/hue.py b/src/hue.py index 5b530db..3fc0ace 100644 --- a/src/hue.py +++ b/src/hue.py @@ -26,7 +26,7 @@ class HueApi(FlickrApiInterface): command["sat"] = fixture.brightness * 255 command["hue"] = fixture.hue * 65535 if fixture.fixture.capabilities in [Capabilities.TEMPERATURE]: - command["ct"] = fixture.temperature + command["ct"] = fixture.temperature / 10 # Apply update self.bridge.set_light(fixture.fixture.api_id, command) diff --git a/src/test.ipynb b/src/test.ipynb new file mode 100644 index 0000000..987374e --- /dev/null +++ b/src/test.ipynb @@ -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 +}