Skip to content

Commit 6ca5fee

Browse files
authored
Merge pull request #670 from Andy-Voigt/master
Added star projector example
2 parents f7e11a9 + 3b67b2c commit 6ca5fee

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

examples/star_projector.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""
2+
Works with the Star Projector:
3+
https://www.amazon.com.au/Anyuainiya-Projector-Christmas-Compatible-Adjustable/dp/B0BL9XTLMZ
4+
5+
Author: Andy Voigt
6+
"""
7+
8+
import colorsys
9+
import tinytuya
10+
from typing import Tuple
11+
12+
HSV = Tuple[float, float, float]
13+
MODES = ['manual', 'scene', 'music']
14+
15+
class GalaxyProjectorState:
16+
"""
17+
Data Points (dps):
18+
20 device on/off
19+
51 star_work_mode: manual, scene, music
20+
52 colour_switch (nebula on/off)
21+
53 laser_switch (stars on/off)
22+
54 laser_bright (stars brightness 10-1000)
23+
62 rotation_speed (rotation speed 1-100%)
24+
24 colour_data (nebula hsv)
25+
"""
26+
def __init__(self, dps=None):
27+
self.dps = dps or {}
28+
29+
def update(self, payload):
30+
payload = payload or {'dps': {}}
31+
if 'Err' in payload:
32+
raise Exception(payload)
33+
self.dps.update(payload['dps'])
34+
35+
@property
36+
def device_on(self) -> bool:
37+
return self.dps['20']
38+
39+
@property
40+
def stars_on(self) -> bool:
41+
return self.dps['53'] # laser_switch
42+
43+
@property
44+
def nebula_on(self) -> bool:
45+
return self.dps['52'] # colour_switch
46+
47+
@property
48+
def scene_mode(self) -> str:
49+
return self.dps['51'] # star_work_mode
50+
51+
@property
52+
def stars_brightness_percent(self):
53+
return int((self.dps['54'] - 10) * 100 / (1000 - 10)) # laser_bright
54+
55+
@property
56+
def rotation_speed_percent(self):
57+
return self.dps['62'] # rotation_speed is 1-100%
58+
59+
@property
60+
def nebula_hsv(self) -> HSV:
61+
return tuyahex2hsv(self.dps['24'])
62+
63+
def __repr__(self):
64+
return f'GalaxyProjectorState<{self.parsed_value}>'
65+
66+
@property
67+
def parsed_value(self):
68+
return {k: getattr(self, k) for k in (
69+
'device_on', 'stars_on', 'nebula_on', 'scene_mode',
70+
'stars_brightness_percent', 'rotation_speed_percent', 'nebula_hsv')}
71+
72+
def tuyahex2hsv(val: str):
73+
return tinytuya.BulbDevice._hexvalue_to_hsv(val, bulb="B")
74+
75+
def hsv2tuyahex(h: float, s: float, v: float):
76+
(r, g, b) = colorsys.hsv_to_rgb(h, s, v)
77+
hexvalue = tinytuya.BulbDevice._rgb_to_hexvalue(
78+
r * 255.0, g * 255.0, b * 255.0, bulb='B'
79+
)
80+
return hexvalue
81+
82+
class GalaxyProjector:
83+
def __init__(self, tuya_device_id: str, device_ip_addr: str, tuya_secret_key: str):
84+
self.device = tinytuya.OutletDevice(tuya_device_id, device_ip_addr, tuya_secret_key)
85+
self.device.set_version(3.5)
86+
self.state = GalaxyProjectorState()
87+
self.update_state()
88+
89+
def update_state(self):
90+
self.state.update(self.device.status())
91+
92+
def set_device_power(self, *, on: bool):
93+
self.state.update(self.device.set_value(20, on))
94+
95+
def set_stars_power(self, *, on: bool):
96+
self.state.update(self.device.set_value(53, on))
97+
98+
def set_nebula_power(self, *, on: bool):
99+
self.state.update(self.device.set_value(52, on))
100+
101+
def set_rotation_speed(self, *, percent: float):
102+
percent = max(1, min(100, int(percent)))
103+
self.state.update(self.device.set_value(62, percent))
104+
105+
def set_stars_brightness(self, *, percent: float):
106+
percent = max(0, min(100, int(percent)))
107+
value = int(10 + (1000 - 10) * percent / 100)
108+
self.state.update(self.device.set_value(54, value))
109+
110+
def set_nebula_color(self, *, hsv: HSV):
111+
# Scene mode must be 'manual' to set nebula color manually
112+
if self.state.scene_mode != 'manual':
113+
self.set_scene_mode(mode='manual')
114+
h, s, v = hsv
115+
h = max(0.0, min(1.0, h))
116+
s = max(0.0, min(1.0, s))
117+
v = max(0.0, min(1.0, v))
118+
hexvalue = hsv2tuyahex(h, s, v)
119+
self.state.update(self.device.set_value(24, hexvalue))
120+
121+
def set_scene_mode(self, *, mode: str):
122+
if mode in MODES:
123+
self.state.update(self.device.set_value(51, mode))
124+
125+
if __name__ == '__main__':
126+
proj = GalaxyProjector(
127+
tuya_device_id=input('Tuya Device ID: '),
128+
device_ip_addr=input('Device IP Addr: '),
129+
tuya_secret_key=input('Tuya Device Secret/Local Key: ')
130+
)
131+
print()
132+
print('Current state:', proj.state.parsed_value)
133+
print()
134+
135+
input('Turn stars off (press enter)')
136+
proj.set_device_power(on=True)
137+
proj.set_stars_power(on=False)
138+
139+
input('Turn stars on (press enter)')
140+
proj.set_stars_power(on=True)
141+
142+
input('Set stars brightness to 100% (press enter)')
143+
proj.set_stars_brightness(percent=100)
144+
145+
input('Set stars brightness to 0% (minimal) (press enter)')
146+
proj.set_stars_brightness(percent=0)
147+
148+
input('Set rotation speed to 100% (press enter)')
149+
proj.set_rotation_speed(percent=100)
150+
151+
input('Set rotation speed to 0% (press enter)')
152+
proj.set_rotation_speed(percent=0)
153+
154+
input('Set nebula color to red (press enter)')
155+
proj.set_nebula_color(hsv=(0, 1, 1))
156+
157+
input('Reduce nebula brightness (press enter)')
158+
proj.set_nebula_color(hsv=(0, 1, 0.3))
159+
160+
input('Turn device off (press enter)')
161+
proj.set_device_power(on=False)

0 commit comments

Comments
 (0)