Initial commit
This commit is contained in:
0
huecli/__init__.py
Normal file
0
huecli/__init__.py
Normal file
175
huecli/__main__.py
Normal file
175
huecli/__main__.py
Normal file
@@ -0,0 +1,175 @@
|
||||
from enum import Enum
|
||||
from typing_extensions import Annotated
|
||||
import typer
|
||||
import json
|
||||
import paho.mqtt.publish as pub
|
||||
|
||||
|
||||
class LightState(str, Enum):
|
||||
on = "off"
|
||||
off = "on"
|
||||
|
||||
|
||||
NAME_TO_ID = {
|
||||
"bedroom": "0x001788010d1b599a",
|
||||
"hallway": "0x001788010d253b99",
|
||||
"office": "0x001788010e371aa4",
|
||||
"all": "all_lights",
|
||||
"infuse": "infuse_group",
|
||||
}
|
||||
COLOR_MAP = {
|
||||
"red": (0.6942, 0.2963),
|
||||
"blue": (0.1355, 0.0399),
|
||||
"green": (0.1704, 0.709),
|
||||
"yellow": (0.4339, 0.5008),
|
||||
"coolest": (0.3131, 0.3232),
|
||||
"cool": (0.3804, 0.3767),
|
||||
"neutral": (0.4599, 0.4106),
|
||||
"warm": (0.5056, 0.4152),
|
||||
"warmest": (0.5267, 0.4133),
|
||||
}
|
||||
MQTT_BROKER = "mqtt://ha1:1183"
|
||||
|
||||
|
||||
class LightID(str, Enum):
|
||||
bedroom = "bedroom"
|
||||
hallway = "hallway"
|
||||
office = "office"
|
||||
all = "all"
|
||||
infuse = "infuse"
|
||||
|
||||
|
||||
def complete_color(incomplete: str):
|
||||
completion = []
|
||||
for color in COLOR_MAP:
|
||||
if color.startswith(incomplete):
|
||||
completion.append(color)
|
||||
return completion
|
||||
|
||||
|
||||
def complete_id(incomplete: str):
|
||||
completion = []
|
||||
for id in NAME_TO_ID:
|
||||
if id.startswith(incomplete):
|
||||
completion.append(id)
|
||||
return completion
|
||||
|
||||
def complete_state(incomplete: str):
|
||||
states = ["on", "off"]
|
||||
completion = []
|
||||
for id in states:
|
||||
if id.startswith(incomplete):
|
||||
completion.append(id)
|
||||
return completion
|
||||
|
||||
|
||||
def rgb_to_xy(red, green, blue):
|
||||
"""conversion of RGB colors to CIE1931 XY colors
|
||||
Formulas implemented from: https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
|
||||
|
||||
Args:
|
||||
red (float): a number between 0.0 and 1.0 representing red in the RGB space
|
||||
green (float): a number between 0.0 and 1.0 representing green in the RGB space
|
||||
blue (float): a number between 0.0 and 1.0 representing blue in the RGB space
|
||||
|
||||
Returns:
|
||||
xy (list): x and y
|
||||
"""
|
||||
|
||||
# gamma correction
|
||||
red = pow((red + 0.055) / (1.0 + 0.055), 2.4) if red > 0.04045 else (red / 12.92)
|
||||
green = (
|
||||
pow((green + 0.055) / (1.0 + 0.055), 2.4)
|
||||
if green > 0.04045
|
||||
else (green / 12.92)
|
||||
)
|
||||
blue = (
|
||||
pow((blue + 0.055) / (1.0 + 0.055), 2.4) if blue > 0.04045 else (blue / 12.92)
|
||||
)
|
||||
|
||||
# convert rgb to xyz
|
||||
x = red * 0.649926 + green * 0.103455 + blue * 0.197109
|
||||
y = red * 0.234327 + green * 0.743075 + blue * 0.022598
|
||||
z = green * 0.053077 + blue * 1.035763
|
||||
|
||||
# convert xyz to xy
|
||||
x = x / (x + y + z)
|
||||
y = y / (x + y + z)
|
||||
|
||||
# TODO check color gamut if known
|
||||
|
||||
return [x, y]
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
@app.command()
|
||||
def set_color(
|
||||
id: Annotated[
|
||||
LightID, typer.Option(help="ID of light.", autocompletion=complete_id)
|
||||
],
|
||||
color: Annotated[
|
||||
str, typer.Option(help="Color to set", autocompletion=complete_color)
|
||||
],
|
||||
):
|
||||
"""
|
||||
Set the color of ID to COLOR where COLOR is either a known color value,
|
||||
or a comma separated RGB value, like "0.1,0.2,0.3".
|
||||
"""
|
||||
str_id = NAME_TO_ID[id]
|
||||
topic = f"zigbee2mqtt/{str_id}/set"
|
||||
if color in COLOR_MAP:
|
||||
payload_raw = {
|
||||
"color": {"x": COLOR_MAP[color][0], "y": COLOR_MAP[color][1]},
|
||||
"color_mode": "xy",
|
||||
}
|
||||
payload = json.dumps(payload_raw)
|
||||
else:
|
||||
c = color.split(",")
|
||||
xy = rgb_to_xy(float(c[0]), float(c[1]), float(c[2]))
|
||||
raw_payload = {"x": xy[0], "y": xy[1]}
|
||||
payload = json.dumps(raw_payload)
|
||||
pub.single(topic, payload, hostname="ha1")
|
||||
|
||||
|
||||
@app.command()
|
||||
def set_state(
|
||||
id: Annotated[
|
||||
LightID, typer.Option(help="ID of light.", autocompletion=complete_id)
|
||||
],
|
||||
state: Annotated[
|
||||
LightState, typer.Option(help="State of light.", autocompletion=complete_state)
|
||||
]):
|
||||
"""
|
||||
Set the state of ID to STATE
|
||||
"""
|
||||
str_id = NAME_TO_ID[id]
|
||||
topic = f"zigbee2mqtt/{str_id}/set"
|
||||
payload = json.dumps({"state": state.value})
|
||||
pub.single(topic, payload, hostname="ha1")
|
||||
|
||||
|
||||
@app.command()
|
||||
def set_brightness(
|
||||
id: Annotated[
|
||||
LightID, typer.Option(help="ID of light.", autocompletion=complete_id)
|
||||
],
|
||||
brightness: int):
|
||||
"""
|
||||
Set brigthness of ID to BRIGHTNESS
|
||||
"""
|
||||
if brightness < 0 or brightness > 255:
|
||||
raise ValueError("Brightness must be between 0 and 255")
|
||||
str_id = NAME_TO_ID[id]
|
||||
topic = f"zigbee2mqtt/{str_id}/set"
|
||||
payload = json.dumps({"brightness": brightness})
|
||||
pub.single(topic, payload, hostname="ha1")
|
||||
|
||||
|
||||
def main():
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user