class InfoPoints(enum.Enum): """ Enum used to determine what information is required for what keys """ # We need to know what kind of product the device is to get the label correctly VERSION = Point(DeviceMessages.GetVersion(), ["label", "product_id", "cap"], None) # We get Label from LIGHT_STATE for lights and from STATE_LABEL for non lights LIGHT_STATE = Point( LightMessages.GetColor(), ["label", "power", "hue", "saturation", "brightness", "kelvin"], 10, # Non lights are unimportant enough at this time to risk sending too many messages to them condition=lambda device: device.product_type is not DeviceType.NON_LIGHT, ) LABEL = Point( DeviceMessages.GetLabel(), ["label"], 10, condition=lambda device: device.product_type is DeviceType.NON_LIGHT, ) FIRMWARE = Point(DeviceMessages.GetHostFirmware(), ["firmware_version"], 300) GROUP = Point(DeviceMessages.GetGroup(), ["group_id", "group_name"], 60) LOCATION = Point(DeviceMessages.GetLocation(), ["location_id", "location_name"], 60)
async def doit(collector): lan_target = collector.resolve_target("lan") async for pkt in lan_target.send(DeviceMessages.GetVersion(), FoundSerials()): if pkt | DeviceMessages.StateVersion: product = Products[pkt.vendor, pkt.product] print(f"{pkt.serial}: {product.as_dict()}")
async def tile_serials_from_reference(reference, sender): """ Given a reference, return all the serials that has a ``has_matrix`` capability """ serials = [] async for pkt in sender(DeviceMessages.GetVersion(), reference): if pkt | DeviceMessages.StateVersion: if Products[pkt.vendor, pkt.product].cap.has_matrix: serials.append(pkt.serial) return serials
class InfoPoints(enum.Enum): """ Enum used to determine what information is required for what keys """ LIGHT_STATE = Point( LightMessages.GetColor(), ["label", "power", "hue", "saturation", "brightness", "kelvin"]) VERSION = Point(DeviceMessages.GetVersion(), ["product_id", "product_identifier", "cap"]) FIRMWARE = Point(DeviceMessages.GetHostFirmware(), ["firmware_version"]) GROUP = Point(DeviceMessages.GetGroup(), ["group_id", "group_name"]) LOCATION = Point(DeviceMessages.GetLocation(), ["location_id", "location_name"])
async def tile_serials_from_reference(target, reference, afr): """ Given a reference, return all the serials that has a ``has_chain`` capability """ serials = [] async for pkt, _, _ in target.script(DeviceMessages.GetVersion()).run_with( reference, afr): if pkt | DeviceMessages.StateVersion: cap = capability_for_ids(pkt.product, pkt.vendor) if cap.has_chain: serials.append(pkt.serial) return serials
class VersionPlan(Plan): """ Return in a dictionary: * vendor: The vendor id of the device. * product: The product id of the device. """ messages = [DeviceMessages.GetVersion()] class Instance(Plan.Instance): def process(self, pkt): if pkt | DeviceMessages.StateVersion: self.dct = pkt.payload.as_dict() return True async def info(self): return self.dct
async def do_apply_theme(target, reference, afr, options): aps = appliers[options.theme] theme = Theme() for color in options.colors: theme.add_hsbk(color.hue, color.saturation, color.brightness, color.kelvin) tasks = [] async for pkt, _, _ in target.script(DeviceMessages.GetVersion()).run_with( reference, afr): serial = pkt.serial capability = capability_for_ids(pkt.product, pkt.vendor) if capability.has_multizone: log.info(hp.lc("Found a strip", serial=serial)) t = hp.async_as_background( apply_zone(aps["1d"], target, afr, pkt.serial, theme, options.overrides)) elif capability.has_chain: log.info(hp.lc("Found a tile", serial=serial)) t = hp.async_as_background( apply_tile(aps["2d"], target, afr, pkt.serial, theme, options.overrides)) else: log.info(hp.lc("Found a light", serial=serial)) t = hp.async_as_background( apply_light(aps["0d"], target, afr, pkt.serial, theme, options.overrides)) tasks.append((serial, t)) results = {} for serial, t in tasks: try: await t except Exception as error: results[serial] = error else: results[serial] = "ok" return results
class CapabilityPlan(Plan): """ Return capability information for this device:: { "cap": <capability object with filled out firmware version>, "product": <product object>, "firmware": <object with build/version_major/version_minor attributes>, "state_version": <the StateVersion packet we received from the device> } The capability and product objects come from the :ref:`registry <products_root>`. """ messages = [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()] class Instance(Plan.Instance): def process(self, pkt): if pkt | DeviceMessages.StateHostFirmware: self.firmware = FirmwareInfo( build=pkt.build, version_major=pkt.version_major, version_minor=pkt.version_minor, ) elif pkt | DeviceMessages.StateVersion: self.version = pkt return hasattr(self, "firmware") and hasattr(self, "version") async def info(self): product = Products[self.version.vendor, self.version.product] cap = product.cap(self.firmware.version_major, self.firmware.version_minor) return { "cap": cap, "product": product, "firmware": self.firmware, "state_version": DeviceMessages.StateVersion.Payload.create( self.version.payload.pack() ), }
async def doit(): async for pkt, _, _ in lan_target.script( DeviceMessages.GetVersion()).run_with(FoundSerials()): if pkt | DeviceMessages.StateVersion: cap = capability_for_ids(pkt.product, pkt.vendor) print("{}: {}".format(pkt.serial, cap))
async for serial, cap in find_multizone(devices.serials, sender): assert serial not in got got[serial] = cap.has_extended_multizone assert got == { striplcm1.serial: False, striplcm2noextended.serial: False, striplcm2extended.serial: True, } async it "resends messages each time if we reset the gatherer", sender: async for serial, cap in find_multizone(devices.serials, sender): pass want = { device: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()] for device in devices } self.compare_received(want) del sender.gatherer async for serial, cap in find_multizone(devices.serials, sender): pass want = { device: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()] for device in devices } self.compare_received(want) async it "uses cached gatherer on the sender", sender:
expected = { clean.serial: (True, {"zones": Skip}), switch.serial: (True, {"zones": Skip}), light1.serial: (True, {"zones": Skip}), light2.serial: (True, {"zones": Skip}), striplcm1.serial: (True, {"zones": [(i, c) for i, c in enumerate(zones1)]}), striplcm2noextended.serial: ( 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(),
assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == sorted(V.serials) for device in V.devices: V.devices.store(device).assertIncoming(DiscoveryMessages.GetService()) V.devices.store(device).clear() reference = DeviceFinder.from_kwargs(label="kitchen") found, ss = await reference.find(V.sender, timeout=5) reference.raise_on_missing(found) assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == [] for device in V.devices: expected = [ DiscoveryMessages.GetService(), DeviceMessages.GetVersion(), LightMessages.GetColor(), ] if device is V.devices["d4"]: expected.append(DeviceMessages.GetLabel) V.devices.store(device).assertIncoming(*expected) V.devices.store(device).clear() await V.d3.change_one("label", "kitchen", event=None) reference = DeviceFinder.from_kwargs(label="kitchen") found, ss = await reference.find(V.sender, timeout=5) reference.raise_on_missing(found) assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == [V.d3.serial] for device in V.devices:
V.received(LightMessages.GetColor()) V.assertTimes({InfoPoints.LIGHT_STATE: 1}) V.t.add(5) assert not (await V.matches(Filter.from_kwargs(label="den"))) V.received() V.assertTimes({InfoPoints.LIGHT_STATE: 1}) V.t.add(2) assert not (await V.matches(Filter.from_kwargs(label="attic", refresh_info=True))) V.received(LightMessages.GetColor()) V.assertTimes({InfoPoints.LIGHT_STATE: 8}) V.t.add(1) assert not (await V.matches(Filter.from_kwargs(group_name="aa", cap=["matrix"]))) V.received(DeviceMessages.GetVersion(), DeviceMessages.GetGroup()) V.assertTimes({InfoPoints.LIGHT_STATE: 8, InfoPoints.GROUP: 9, InfoPoints.VERSION: 9}) V.t.add(2) # It never refreshes version assert not ( await V.matches(Filter.from_kwargs(group_name="aa", cap=["matrix"], refresh_info=True)) ) V.received(DeviceMessages.GetGroup()) V.assertTimes({InfoPoints.LIGHT_STATE: 8, InfoPoints.GROUP: 11, InfoPoints.VERSION: 9}) V.t.add(3) assert await V.matches(Filter.from_kwargs(cap=["not_matrix"], refresh_info=True)) V.received() V.assertTimes({InfoPoints.LIGHT_STATE: 8, InfoPoints.GROUP: 11, InfoPoints.VERSION: 9})
assert len(devices) > 0 for device in devices: if device not in expected: assert False, f"No expectation for {device.serial}" for device, msgs in expected.items(): assert device in devices devices.store(device).assertIncoming(*msgs, ignore=[DiscoveryMessages.GetService]) devices.store(device).clear() async it "sets the default config", sender: expected = { light1: [ DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion(), LightMessages.SetHevCycleConfiguration(indication=True, duration_s=3600), ], light2: [ DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion(), LightMessages.SetHevCycleConfiguration(indication=True, duration_s=3600), ], light3: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()], } await self.run_and_compare( sender, SetCleanConfig(indication=True, duration_s=3600), expected=expected ) describe "ChangeCleanCycle":
async it "complains if we have more than 16 colors in the palette", runner: with assertRaises(PhotonsAppError, "Palette can only be up to 16 colors", got=17): SetTileEffect("flame", palette=["red"] * 17) async it "can power on devices and set tile effect", runner: msg = SetTileEffect("flame") got = await runner.sender(msg, runner.serials) assert got == [] for tile in tiles: assert tile.attrs.matrix_effect is TileEffectType.FLAME self.compare_received( { nottile: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()], tile1: [ DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion(), LightMessages.SetLightPower(level=65535, duration=1), TileMessages.SetTileEffect.empty_normalise( type=TileEffectType.FLAME, palette=default_tile_palette, palette_count=len(default_tile_palette), ), ], tile2: [ DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion(), LightMessages.SetLightPower(level=65535, duration=1), TileMessages.SetTileEffect.empty_normalise(
V.received(DeviceMessages.GetGroup()) V.assertTimes({InfoPoints.LABEL: 8, InfoPoints.GROUP: 11, InfoPoints.VERSION: 1}) V.t.add(3) assert await V.matches(Filter.from_kwargs(cap=["not_matrix"], refresh_info=True)) V.received() V.assertTimes({InfoPoints.LABEL: 8, InfoPoints.GROUP: 11, InfoPoints.VERSION: 1}) async it "can start an information loop", fake_time, sender, finder, final_future: V = VBase(fake_time, sender, finder, final_future) await V.choose_device("light") fake_time.set(1) msgs = [e.value.msg for e in list(InfoPoints) if e is not InfoPoints.LABEL] assert msgs == [ DeviceMessages.GetVersion(), LightMessages.GetColor(), DeviceMessages.GetHostFirmware(), DeviceMessages.GetGroup(), DeviceMessages.GetLocation(), ] message_futs = {} class Futs: pass class Waiter: def __init__(s, name, kls): s.name = name s.kls = kls
reference.raise_on_missing(found) assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == [V.d3.serial] for device in V.devices: device.compare_received([LightMessages.GetColor()]) device.reset_received() reference = DeviceFinder.from_kwargs(cap="matrix") found, ss = await reference.find(runner.sender, timeout=5) reference.raise_on_missing(found) assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == [V.d1.serial] for device in V.devices: device.compare_received([DeviceMessages.GetVersion()]) device.reset_received() reference = DeviceFinder.from_kwargs(cap=["matrix", "multizone"]) found, ss = await reference.find(runner.sender, timeout=5) reference.raise_on_missing(found) assert sorted(list(found)) == sorted(binascii.unhexlify(s)[:6] for s in ss) assert ss == [V.d1.serial, V.d2.serial] for device in V.devices: device.compare_received([DeviceMessages.GetVersion()]) device.reset_received() reference = DeviceFinder.from_kwargs(cap=["not_matrix"], label="kitchen") found, ss = await reference.find(runner.sender, timeout=5) reference.raise_on_missing(found)
for device in devices: if device not in expected: assert False, f"No expectation for {device.serial}" for device, msgs in expected.items(): assert device in devices devices.store(device).assertIncoming(*msgs, ignore=[DiscoveryMessages.GetService]) devices.store(device).clear() async it "sends the messages to devices with only correct capability", sender: msg = ForCapability(hev=LightMessages.GetHevCycle()) expected = { a19: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()], clean: [ DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion(), LightMessages.GetHevCycle(), ], ir: [DeviceMessages.GetHostFirmware(), DeviceMessages.GetVersion()], } await self.assertScript(sender, msg, expected=expected) async it "can send message to groups", sender: msg = ForCapability(**{"ir,hev": DeviceMessages.SetPower(level=65535)}) expected = {