예제 #1
0
async def apply_zone(applier, target, afr, serial, theme, overrides):
    length = None
    msg = MultiZoneMessages.GetColorZones(start_index=0, end_index=255)
    async for pkt, _, _ in target.script(msg).run_with(serial, afr):
        if pkt | MultiZoneMessages.StateMultiZone:
            length = pkt.zones_count

    if length is None:
        log.warning(
            hp.lc("Couldn't work out how many zones the light had",
                  serial=serial))
        return

    messages = []
    for (start_index, end_index), hsbk in applier(length).apply_theme(theme):
        messages.append(
            MultiZoneMessages.SetColorZones(start_index=start_index,
                                            end_index=end_index,
                                            hue=hsbk.hue,
                                            saturation=hsbk.saturation,
                                            brightness=hsbk.brightness,
                                            kelvin=hsbk.kelvin,
                                            duration=overrides.get(
                                                "duration", 1),
                                            res_required=False,
                                            ack_required=True))

    set_power = LightMessages.SetLightPower(level=65535,
                                            duration=overrides.get(
                                                "duration", 1))
    pipeline = Pipeline(*messages, spread=0.005)
    await target.script([set_power, pipeline]).run_with_all(serial, afr)
예제 #2
0
    def get_zones(self, start_index=0, end_index=255):
        buf = []
        bufs = []

        for i, zone in enumerate(self.device.attrs.zones):
            if i < start_index or i > end_index:
                continue

            if len(buf) == 8:
                bufs.append(buf)
                buf = []

            buf.append((i, zone))

        if buf:
            bufs.append(buf)

        for buf in bufs:
            if len(buf) == 1:
                yield MultiZoneMessages.StateZone(
                    zones_count=len(self.device.attrs.zones),
                    zone_index=buf[0][0],
                    **buf[0][1].as_dict(),
                )
                continue

            yield MultiZoneMessages.StateMultiZone(
                zones_count=len(self.device.attrs.zones),
                zone_index=buf[0][0],
                colors=[b.as_dict() for _, b in buf],
            )
예제 #3
0
            def zone_msgs(self, overrides):
                power_message = self.power_message(overrides)
                if power_message:
                    yield power_message

                colors = self.colors_from_hsbks(self.zones, overrides)
                duration = self.determine_duration(overrides)

                start = 0
                color = None
                i = -1
                while i < len(colors) - 1:
                    i += 1
                    if color is None:
                        color = colors[i]
                        continue
                    if colors[i] != color:
                        yield MultiZoneMessages.SetColorZones(
                            start_index=start,
                            end_index=i - 1,
                            **color,
                            duration=duration,
                            res_required=False,
                        )
                        color = colors[i]
                        start = i

                color = colors[i]
                yield MultiZoneMessages.SetColorZones(start_index=start,
                                                      end_index=i,
                                                      **color,
                                                      duration=duration,
                                                      res_required=False)
예제 #4
0
 def messages(self):
     if self.is_multizone:
         if self.has_extended_multizone:
             return [MultiZoneMessages.GetExtendedColorZones()]
         else:
             return [MultiZoneMessages.GetColorZones(start_index=0, end_index=255)]
     return Skip
예제 #5
0
async def zones_from_reference(target,
                               reference,
                               afr=sb.NotSpecified,
                               **kwargs):
    """
    Return a dictionary of {serial: [(zone_index, colors), ...]} for the provided reference

    We assume all the devices support multizone
    """
    final = {}

    msg = MultiZoneMessages.GetColorZones(start_index=0, end_index=255)
    options = MergedOptions.using({"timeout": 5}, kwargs).as_dict()

    by_serial = defaultdict(list)
    async for pkt, _, _ in target.script(msg).run_with(reference, afr,
                                                       **options):
        by_serial[pkt.serial].append(pkt)

    for serial, pkts in by_serial.items():
        final[serial] = []
        for p in pkts:
            if p | MultiZoneMessages.StateMultiZone:
                for i, color in enumerate(p.colors):
                    final[serial].append((p.zone_index + i, color))

    return final
예제 #6
0
    async def execute(self):
        fltr = chp.filter_from_matcher(self.matcher, self.refresh)
        details = await self.finder.info_for(filtr=fltr)

        msgs = []
        for serial, info in details.items():
            msgs.append(DeviceMessages.GetPower(target=serial))

            if "multizone" in info["cap"]:
                msgs.append(
                    MultiZoneMessages.GetColorZones(start_index=0, end_index=255, target=serial)
                )
            elif "chain" in info["cap"]:
                msgs.append(
                    TileMessages.Get64(tile_index=0, length=5, x=0, y=0, width=8, target=serial)
                )
            else:
                msgs.append(LightMessages.GetColor(target=serial))

        state = defaultdict(dict)
        afr = await self.finder.args_for_run()
        async for pkt, _, _ in self.target.script(msgs).run_with(
            None, afr, multiple_replies=True, first_wait=0.5
        ):
            if pkt | DeviceMessages.StatePower:
                state[pkt.serial]["power"] = pkt.level != 0
            elif pkt | LightMessages.LightState:
                hsbk = f"kelvin:{pkt.kelvin} saturation:{pkt.saturation} brightness:{pkt.brightness} hue:{pkt.hue}"
                state[pkt.serial]["color"] = hsbk
            elif pkt | MultiZoneMessages.StateMultiZone:
                if "zones" not in state[pkt.serial]:
                    state[pkt.serial]["zones"] = {}
                for i, zi in enumerate(range(pkt.zone_index, pkt.zone_index + 8)):
                    c = pkt.colors[i]
                    state[pkt.serial]["zones"][zi] = [c.hue, c.saturation, c.brightness, c.kelvin]
            elif pkt | TileMessages.State64:
                if "chain" not in state[pkt.serial]:
                    state[pkt.serial]["chain"] = {}
                colors = [[c.hue, c.saturation, c.brightness, c.kelvin] for c in pkt.colors]
                state[pkt.serial]["chain"][pkt.tile_index] = colors

        scene = []
        for serial, info in sorted(state.items()):
            if "zones" in info:
                info["zones"] = [hsbk for _, hsbk in sorted(info["zones"].items())]
            if "chain" in info:
                info["chain"] = [hsbks for _, hsbks in sorted(info["chain"].items())]

            scene.append({"matcher": {"serial": serial}, **info})

        if self.just_return:
            return scene

        args = {
            "uuid": self.uuid,
            "scene": scene,
            "label": self.label,
            "description": self.description,
        }
        return await self.executor.execute(self.path, {"command": "scene_change", "args": args})
예제 #7
0
 def make_hsbk_new_messages(self, zone_index, colors, duration):
     return MultiZoneMessages.SetExtendedColorZones(
         duration=duration,
         colors_count=len(colors),
         colors=colors,
         zone_index=zone_index,
         ack_required=True,
         res_required=False,
     )
예제 #8
0
 def setter(h, s, b, k, **kwargs):
     return MultiZoneMessages.SetColorZones(
         hue=h,
         saturation=s,
         brightness=b,
         kelvin=k,
         res_required=False,
         **kwargs,
     )
예제 #9
0
    def make_hsbk_old_messages(self, zone_index, colors, duration):
        set_color_old = []

        end = zone_index
        start = zone_index
        current = None

        for i, color in enumerate(colors):
            i = i + zone_index

            if current is None:
                current = color
                continue

            if current != color:
                set_color_old.append(
                    MultiZoneMessages.SetColorZones(
                        start_index=start,
                        end_index=end,
                        duration=duration,
                        ack_required=True,
                        res_required=False,
                        **current,
                    ))
                start = i

            current = color
            end = i

        if not set_color_old or set_color_old[-1].end_index != i:
            set_color_old.append(
                MultiZoneMessages.SetColorZones(
                    start_index=start,
                    end_index=end,
                    duration=duration,
                    ack_required=True,
                    res_required=False,
                    **current,
                ))

        return set_color_old
예제 #10
0
    def make_old_messages(self):
        if not self.colors:
            return

        end = self.zone_index
        start = self.zone_index

        current = Empty

        sections = []

        for i, color in enumerate(self.colors):
            i = i + self.zone_index

            if current is Empty:
                current = color
                continue

            if current != color:
                sections.append([start, end, current])
                start = i
                start = i

            current = color
            end = i

        if current is not Empty and (not sections or sections[-1][1] != i):
            sections.append([start, end, current])

        msgs = []
        for start, end, color in sections:
            h, s, b, k = color

            msgs.append(
                MultiZoneMessages.SetColorZones(
                    start_index=start,
                    end_index=end,
                    duration=self.duration,
                    ack_required=True,
                    res_required=False,
                    target=self.serial,
                    hue=h,
                    saturation=s,
                    brightness=b,
                    kelvin=k,
                ))
        return msgs
예제 #11
0
    def make_new_messages(self):
        if not self.colors:
            return

        colors = []
        for c in self.colors:
            if isinstance(c, dict) or getattr(c, "is_dict", False):
                colors.append(c)
            else:
                colors.append(Color(*c))

        return (MultiZoneMessages.SetExtendedColorZones(
            duration=self.duration,
            colors_count=len(self.colors),
            colors=colors,
            target=self.serial,
            zone_index=self.zone_index,
            ack_required=True,
            res_required=False,
        ), )
예제 #12
0
                    True,
                    {"zones": [(i, c) for i, c in enumerate(zones2)]},
                ),
                striplcm2extended.serial: (True, {"zones": [(i, c) for i, c in enumerate(zones3)]}),
            }
            assert got == expected

            expected = {
                clean: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()],
                switch: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()],
                light1: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()],
                light2: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()],
                striplcm1: [
                    DeviceMessages.GetHostFirmware(),
                    DeviceMessages.GetVersion(),
                    MultiZoneMessages.GetColorZones(start_index=0, end_index=255),
                ],
                striplcm2noextended: [
                    DeviceMessages.GetHostFirmware(),
                    DeviceMessages.GetVersion(),
                    MultiZoneMessages.GetColorZones(start_index=0, end_index=255),
                ],
                striplcm2extended: [
                    DeviceMessages.GetHostFirmware(),
                    DeviceMessages.GetVersion(),
                    MultiZoneMessages.GetExtendedColorZones(),
                ],
            }

            for device in devices:
                if device not in expected:
예제 #13
0
describe "Zones":

    async def make_device(self, name, zones=None):
        device = devices[name]
        if zones is not None:
            await device.change_one("zones", zones, event=None)
        return device

    async it "doesn't respond if we aren't a multizone device":
        device = devices["a19"]
        assert "zones_effect" not in device.attrs
        assert "zones" not in device.attrs

        assertUnhandled = makeAssertUnhandled(device)

        await assertUnhandled(MultiZoneMessages.GetMultiZoneEffect())
        await assertUnhandled(
            MultiZoneMessages.SetMultiZoneEffect.create(
                type=MultiZoneEffectType.MOVE, parameters={}
            )
        )

        await assertUnhandled(MultiZoneMessages.GetColorZones(start_index=0, end_index=255))
        await assertUnhandled(
            MultiZoneMessages.SetColorZones(
                start_index=0, end_index=1, hue=0, saturation=1, brightness=1, kelvin=3500
            )
        )

        await assertUnhandled(MultiZoneMessages.GetExtendedColorZones())
        await assertUnhandled(
예제 #14
0
 def messages(self):
     if self.zones is Zones.MATRIX:
         return [TileMessages.GetDeviceChain()]
     elif self.zones is Zones.LINEAR:
         return [MultiZoneMessages.GetColorZones(start_index=0, end_index=0)]
     return []
예제 #15
0
            return Device(
                chp.ProductResponder.from_product(enum, firmware), chp.ZonesResponder(zones=zones),
            )

        async it "complains if you try to set too many zones":
            zones = [chp.Color(0, 0, 0, 0)] * 83
            with assertRaises(PhotonsAppError, "Can only have up to 82 zones!"):
                async with self.make_device(Products.LCM1_Z, chp.Firmware(0, 0, 0), zones):
                    pass

        async it "doesn't respond if we aren't a multizone device":
            async with self.make_device(Products.LCMV4_A19_COLOR, chp.Firmware(0, 0, 0)) as device:
                assert "zones_effect" not in device.attrs
                assert "zones" not in device.attrs

                await device.assertResponse(MultiZoneMessages.GetMultiZoneEffect(), [])
                await device.assertResponse(
                    MultiZoneMessages.SetMultiZoneEffect.empty_normalise(
                        type=MultiZoneEffectType.MOVE, parameters={}
                    ),
                    [],
                )

                await device.assertResponse(
                    MultiZoneMessages.GetColorZones(start_index=0, end_index=255), []
                )
                await device.assertResponse(
                    MultiZoneMessages.SetColorZones(
                        start_index=0, end_index=1, hue=0, saturation=1, brightness=1, kelvin=3500
                    ),
                    [],
예제 #16
0
            assert got == {
                striplcm1.serial: [(i, c) for i, c in enumerate(zones1)],
                striplcm2noextended.serial: [(i, c) for i, c in enumerate(zones2)],
                striplcm2extended.serial: [(i, c) for i, c in enumerate(zones3)],
            }

        async it "resends messages if no gatherer is reset between runs", sender:
            async for serial, zones in zones_from_reference(devices.serials, sender):
                pass

            want = {
                device: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()]
                for device in devices
            }
            want[striplcm1].append(MultiZoneMessages.GetColorZones(start_index=0, end_index=255))
            want[striplcm2noextended].append(
                MultiZoneMessages.GetColorZones(start_index=0, end_index=255)
            )
            want[striplcm2extended].append(MultiZoneMessages.GetExtendedColorZones())
            self.compare_received(want)

            del sender.gatherer
            async for serial, zones in zones_from_reference(devices.serials, sender):
                pass

            self.compare_received(want)

        async it "uses cached gatherer on the sender", sender:
            async for serial, zones in zones_from_reference(devices.serials, sender):
                pass
예제 #17
0
 def messages(self):
     if self.is_multizone:
         return [MultiZoneMessages.GetMultiZoneEffect()]
     elif self.is_matrix:
         return [TileMessages.GetTileEffect()]
     return Skip
예제 #18
0
                f"SetColor(ack=True,res=False,source={pkt.source},sequence=4,target=d073d5003333)",
                mock.ANY,
                "  hue: 200.0",
                "  saturation: 1.0",
                "  brightness: 0.5",
                "  kelvin: 9000",
                "  duration: 0.0",
            )

        it "can format messages with lists":
            pkt = MultiZoneMessages.SetExtendedColorZones(
                ack_required=False,
                res_required=False,
                sequence=5,
                target="d073d5004444",
                colors_count=3,
                colors=[
                    {"hue": 3, "brightness": 1, "saturation": 1, "kelvin": 9000},
                    {"hue": 20, "saturation": 1},
                    {"hue": 30, "saturation": 1, "brightness": 0.2},
                ],
            )
            self.assertLines(
                pkt,
                "SetExtendedColorZones(ack=False,res=False,source=<NOT_SPECIFIED>,sequence=5,target=d073d5004444)",
                "  duration: 0.0",
                "  apply: <MultiZoneExtendedApplicationRequest.APPLY: 1>",
                "  zone_index: <NOT_SPECIFIED>",
                "  colors_count: 3",
                "  colors:",
                "  {'brightness': 1.0, 'hue': 3.0, 'kelvin': 9000, 'saturation': 1.0}",
                "  {'brightness': '<NOT_SPECIFIED>', 'hue': 20.0, 'kelvin': 3500, 'saturation': 1.0}",
예제 #19
0
                    device.io["MEMORY"],
                    pkt=CoreMessages.Acknowledgement,
                    replying_to=original,
                ),
                devices.Events.OUTGOING(
                    device, device.io["MEMORY"], pkt=expected, replying_to=original
                ),
            ]

        async it "can get multiple replies", send_single, device:
            await device.event(
                devices.Events.SET_ZONES, zones=[(i, hp.Color(i, 1, 1, 3500)) for i in range(22)]
            )
            devices.store(device).clear()

            original = MultiZoneMessages.GetColorZones(start_index=0, end_index=255)
            expected = [
                (
                    MultiZoneMessages.StateMultiZone,
                    {
                        "zones_count": 22,
                        "zone_index": 0,
                        "colors": [hp.Color(i, 1, 1, 3500) for i in range(8)],
                    },
                ),
                (
                    MultiZoneMessages.StateMultiZone,
                    {
                        "zones_count": 22,
                        "zone_index": 8,
                        "colors": [hp.Color(i, 1, 1, 3500) for i in range(8, 16)],