Python - XY 颜色值无法使用 Philips Hue Entertainment API(通过 DTLS/PSK)正确发送

标签 python colors philips-hue mbedtls

编辑以添加包含完整类的 github 存储库的链接:

https://github.com/EvillerBobUK/pyHue-BridgeLink-Example

需要您拥有 Philips Hue 硬件和各种 DTSL/PSK 软件包才能使示例正常工作


我正在用 Python 3 编写一个小程序,在 Raspberry Pi 上的 Ubuntu 中运行,通过 Philips Hue 桥接器(版本 2)向智能灯发送指令。

该程序可以在除一种模式之外的大多数模式下运行(详细信息如下),因此我相信连接本身运行正常。我认为问题在于特定模式的数据报的构造 - 根据文档,可能与桥仅在该模式下使用 12 位分辨率的 X 和 Y float 有关? - 但这是一个我真的不熟悉的领域,我只是看不出我哪里出错了!

PH API 具有“标准”模式(指令通过 https 发送)和“娱乐”模式(指令使用 dtls/psk 通过 UDP 广播)。娱乐模式支持以 RGB 或 XY + 亮度形式发送数据,任一版本均相当于 3 x 16 位数据类型。 API 指南中将 Endianess 指定为“Big-Endian(网络)”; BigEndian 和网络都已尝试并给出相同的结果。

RGB 以 0-254 之间的三个整数形式发送。

XY 作为 0.0-1.0 之间的两个 float 发送。 API 文档似乎建议亮度应作为 0-254 之间的整数发送,但这是行不通的。经过实验,将其作为 0.0-1.0 之间的 float 发送可以按预期工作。

我正在使用从互联网上某处抓取的一段代码来创建 DTS/PSK 连接和打包结构(我确实在处理整个 DTLS/PSK/Datagram 问题),尽管它只是被使用如果我记得的话,代码中的 RGB 。根据我对 Philips Hue API 指南的理解,该结构是以相同的方式构建和发送的,只是更改了我所做的颜色空间标志和数据类型,所以理论上它应该可以工作?

如果我使用标准模式通过 https 发送指令 {"xy":[0.6915,0.3083],"bri":254},指示灯会按预期变为红色。

如果我使用娱乐模式通过 UDP 发送指令 [(3, 255, 0, 0)],'RGB',指示灯会按预期变为红色,并且 Philips Hue API 调试 CLIP工具按其应有的方式报告 "xy": [0.6915,0.3083]

如果我使用娱乐模式通过 UDP 发送指令 [(3, 0.6915,0.3083, 0.1)],'XYB',指示灯会变成非常白的蓝色,并且 Philips Hue API调试工具改为报告 "xy": [0.2245,0.2065]!

使用 0.0-1.0 之间的不同值似乎总是以某种淡蓝色结束。有一次,我运行了一个循环,将数字推向各个方向并超出预期范围,这确实导致偶尔出现黄色、绿色,很少出现红色,但通常是在发送的值大大超出范围/负值时。这些深夜绝望的测试都没有被正确记录或指出我在结果中看到的任何类型的模式/逻辑!

对于理解问题并解决问题的任何帮助,我们将不胜感激!

类代码摘录

class pyHue_BridgeLink:
    def __init__(self, bridgename=False,config=False):
        # self.broadcast is a class-level variable for holding the datagram packed struct.      
        self.broadcast = None

    # put is the function that takes the instructions and turns them into a https put request, 
    # which is then sent to the bridge for forwarding to the appropriate light.
    def put(self,url,request,payload,sslverify=False):
        return requests.put(f"{url}{request}", json=payload, verify=sslverify).json() 

    # prepare_broadcast is the function that takes the instructions and turns them into a datagram,
    # which is then sent to the bridge (by a call elsewhere to send_broadcast) for broadcasting to the
    # entertainment light group.
    # Datatype 'e' is, I believe, a 16bit float.
    # Endian type > (BigEndian) and ! (network) have both been tried
    def prepare_broadcast(self, states, colourspace='RGB'): #colourspace = 'RGB' or 'XYB'
        if colourspace == 'XYB':
            cs = 0x01
            datatypes = ">BHeee"
        else:
            cs = 0x00
            datatypes = ">BHHHH"
        count = len(states)
        self.broadcast = bytearray([0]*(16+count*9))
        struct.pack_into(">9s2BB2BBB", self.broadcast, 0,
                         "HueStream".encode('ascii'),  # Protocol Name (fixed)
                         0x01, 0x00,                   # Version (=01.00)
                         0x00,                         # Sequence ID (ignored)
                         0x00, 0x00,                   # Reserved (zeros)
                         cs,                           # Color Space (RGB=0x00, XYB=0x01)
                         0x00                          # Reserved (zero)
                         )
        for i in range(count):  # Step through each set of instructions in "states"
            struct.pack_into(datatypes, self.broadcast, 16 + i*9,
                             0x00,                      # Type: Light
                             states[i][0],              # Light ID
                             states[i][1],              # Red/X
                             states[i][2],              # Blue/Y
                             states[i][3]               # Green/Brightness
                             )

    # send_broadcast passes the prepared datagram held in self.broadcast to the socket
    # for sending via DTLS/PSK which is, as far as I can establish, some kind of witchcraft.
    def send_broadcast(self):
        if self.sock:
            self.sock.send(self.broadcast) 

    # prepare_and_send_broadcast is an intermediary function that, unsurprisingly, calls the
    # prepare_broadcast command above then calls the send_broadcast command. There are cases where the
    # self.broadcast packed struct will be sent at a different time to when it was prepared. This
    # function is used when both need to happen at the same time.
    def prepare_and_send_broadcast(self, states, colourspace='RGB'):
        self.prepare_broadcast(states, colourspace)
        self.send_broadcast()

使用示例

bl = pyHue_BridgeLink("BridgeOne")

# Using the standard API to send via https. This works correctly!
# Physical light turns red as expected.
# Philips Hue API Debug CLIP tool reports "xy": [0.6915,0.3083] as it should.
bl.put(bl.url,'lights/3/state',{"xy":[0.6915,0.3083],"bri":254})

# Switching to Entertainment mode...
bl.enable_streaming()

# Using the entertainment API to stream RGB instructions via UDP. This works correctly!
# Physical light turns red as expected.
# Philips Hue API Debug CLIP tool reports "xy": [0.6915,0.3083] as it should.
bl.prepare_and_send_broadcast([(3, 255,0,0)],'RGB')

# Using the entertainment API to stream XY+Brightness instructions via UDP. This fails!
# Physical light turns a very whitish-blue.
# Philips Hue API Debug CLIP tool reports "xy": [0.2245,0.2065] which is very wrong.
bl.prepare_and_send_broadcast([(3, 0.6915,0.3083, 0.1)],'XYB')

# Switching back out of Entertainment mode...
bl.disable_streaming()

编辑以添加包含完整类的 github 存储库的链接:

https://github.com/EvillerBobUK/pyHue-BridgeLink-Example

需要您拥有 Philips Hue 硬件和各种 DTSL/PSK 软件包才能使示例正常工作

最佳答案

遇到同样的问题并找到解决方案。关键在于 Hue 文档的状态

For xy, the minimum value of 0x0000 is equivalent to 0.00000, and 0xffff is equivalent to 1.00000

因此,要获得正确的十六进制值,您需要类似以下内容来获取两个字节中每个字节的正确值(其中 x 和 y 是十进制值,如“0.6915”/“0.3083”):

x_1, x_2 = int(x * 65535).to_bytes(2, 'big')
y_1, y_2 = int(y * 65535).to_bytes(2, 'big')

然后在 struct.pack_into 片段中将它们作为单独的字节引用(我使用的是 Entertainment API 格式的 V2,但它与上面的代码类似):

struct.pack_into(">9s2BB2BBB36sB6BB6B", light_data, 0,
                     "HueStream".encode('ascii'),  # Protocol Name (fixed)
                     0x02, 0x00,                   # Version (=02.00)
                     0x00,                         # Sequence ID (ignored)
                     0x00, 0x00,                   # Reserved (zeros)
                     0x01,                           # Color Space (RGB=0x00, XYB=0x01)
                     0x00,                          # Reserved (zero)
                     entertainment_id.encode('ascii'), # Entertainment id
                     0x00,                          # Channel 0
                     x_0, x_1, y_0, y_1, b_0, b_1,  # Channel 0 values
                     0x01,                          # Channel 1
                     x_0, x_1, y_0, y_1, b_0, b_1   # Channel 1 values
                     )

关于Python - XY 颜色值无法使用 Philips Hue Entertainment API(通过 DTLS/PSK)正确发送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67433788/

相关文章:

philips-hue - 如何检测飞利浦 Hue 桥上的电源周期?

javascript - 在其他网络上的 Production Web 应用程序中查找 Philips Hue Bridge

r - facet_wrap : mark (color border, 行和标题)一个特定的情节

python - 使用适用于 Python 2.7 的 MacPorts 安装 Shogun 工具箱

python - Numpy 天花板和地板 "out"参数

python - 数组运算等价Python和Matlab

android - 状态栏在全屏对话框 fragment android 中将其颜色更改为黑色

java - 如何将 BufferedImage 中的一种颜色替换为另一种颜色?

python - @csrf_exempt 不适用于基于通用 View 的类