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)
class LabelPlan(Plan): """Return the label of this device""" messages = [DeviceMessages.GetLabel()] default_refresh = 5 class Instance(Plan.Instance): def process(self, pkt): if pkt | DeviceMessages.StateLabel: self.label = pkt.label return True async def info(self): return self.label
async def doit(collector): lan_target = collector.resolve_target("lan") getter = [DeviceMessages.GetLabel(), LightMessages.GetColor()] info = defaultdict(dict) async for pkt in lan_target.send(getter, FoundSerials()): if pkt | DeviceMessages.StateLabel: info[pkt.serial]["label"] = pkt.label elif pkt | LightMessages.LightState: hsbk = " ".join("{0}={1}".format(key, pkt[key]) for key in ("hue", "saturation", "brightness", "kelvin")) info[pkt.serial]["hsbk"] = hsbk for serial, details in info.items(): print(f"{serial}: {details['label']}: {details['hsbk']}")
async def doit(): getter = [DeviceMessages.GetLabel(), LightMessages.GetColor()] def found(serial, *states): info = {"label": "", "hsbk": ""} for s in states: if s | DeviceMessages.StateLabel: info["label"] = s.label elif s | LightMessages.LightState: info["hsbk"] = " ".join("{0}={1}".format(key, s.payload[key]) for key in ("hue", "saturation", "brightness", "kelvin")) print("{0}: {1}: {2}".format(serial, info["label"], info["hsbk"])) return [] msg = Decider(getter, found, [DeviceMessages.StateLabel, LightMessages.LightState]) await lan_target.script(msg).run_with_all(FoundSerials())
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: 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() reference = DeviceFinder.from_kwargs(cap="matrix") 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.d1.serial] for device in V.devices: V.devices.store(device).assertIncoming( DiscoveryMessages.GetService(), DeviceMessages.GetVersion() ) V.devices.store(device).clear()
async def checker(ff): info = {"serial": V.fake_device.serial, "product_type": "unknown"} assert V.device.info == info await hp.wait_for_all_futures( *[ V.device.point_futures[kls] for kls in InfoPoints if kls is not InfoPoints.LIGHT_STATE ] ) found = [] for kls in list(InfoPoints): if kls is not InfoPoints.LIGHT_STATE: found.append(V.device.point_futures[kls].result()) assert found == [1, 2, 3, 4, 5] assert V.t.time == 5 V.received(*msgs) info.update( { "label": "switcharoo", "firmware_version": "3.90", "product_id": 89, "product_name": "LIFX Switch", "product_type": "non_light", "cap": pytest.helpers.has_caps_list("buttons", "unhandled", "relays"), "group_id": "aa000000000000000000000000000000", "group_name": "g1", "location_id": "bb000000000000000000000000000000", "location_name": "l1", } ) assert V.device.info == info await hp.wait_for_all_futures(Futs.label) V.received(DeviceMessages.GetLabel(), keep_duplicates=True) assert V.t.time == 12 await hp.wait_for_all_futures(Futs.group, Futs.location) V.received( *([DeviceMessages.GetLabel()] * 5), DeviceMessages.GetGroup(), DeviceMessages.GetLocation(), keep_duplicates=True, ) # First location was at t=5 # We then wait another 60 # 60 is at 12 rounds, and next location after that is after 5 assert V.t.time == 65 assert V.device.point_futures[InfoPoints.LABEL].result() == 62 await hp.wait_for_all_futures(Futs.label) V.received(DeviceMessages.GetLabel(), keep_duplicates=True) # 62 + 10 = 72 assert V.t.time == 72 await hp.wait_for_all_futures(Futs.firmware) assert V.device.point_futures[InfoPoints.LABEL].result() == 102 assert V.t.time == 103 V.received( DeviceMessages.GetLabel(), DeviceMessages.GetLabel(), DeviceMessages.GetLabel(), DeviceMessages.GetHostFirmware(), keep_duplicates=True, ) ff.cancel()
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: 1}) async it "can match against a fltr for a non light", sender, finder, fake_time, final_future: V = VBase(fake_time, sender, finder, final_future) await V.choose_device("switch") V.t.add(1) assert await V.matches(None) V.received() assert await V.matches(Filter.from_kwargs(label="switcharoo")) V.received(LightMessages.GetColor(), DeviceMessages.GetLabel()) V.assertTimes({InfoPoints.LABEL: 1, InfoPoints.VERSION: 1}) V.t.add(5) assert not (await V.matches(Filter.from_kwargs(label="den"))) V.received() V.assertTimes({InfoPoints.LABEL: 1, InfoPoints.VERSION: 1}) V.t.add(2) assert not (await V.matches(Filter.from_kwargs(label="attic", refresh_info=True))) V.received(DeviceMessages.GetLabel()) V.assertTimes({InfoPoints.LABEL: 8, InfoPoints.VERSION: 1}) V.t.add(1) assert not (await V.matches(Filter.from_kwargs(group_name="aa", cap=["matrix"]))) V.received(DeviceMessages.GetGroup())
def item(): return Item([DeviceMessages.GetPower(), DeviceMessages.GetLabel()])
describe "Responders": describe "LightStateResponder": @pytest.fixture() async def device(self): device = Device(chp.LightStateResponder()) async with device: device.assertAttrs(label="", power=0, color=chp.Color(0, 0, 1, 3500)) yield device async it "responds to label messages", device: await device.assertResponse( DeviceMessages.GetLabel(), [DeviceMessages.StateLabel(label="")] ) await device.assertResponse( DeviceMessages.SetLabel(label="sam"), [DeviceMessages.StateLabel(label="sam")], label="sam", ) await device.assertResponse( DeviceMessages.GetLabel(), [DeviceMessages.StateLabel(label="sam")], label="sam" ) async it "responds to power messages", device: await device.assertResponse( DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=0)] ) await device.assertResponse(
describe "LightDevice": @pytest.fixture() def device(self): device = devices["a19"] devices.store(device).assertAttrs(label="", power=0, color=hp.Color(0, 0, 1, 3500)) return device @pytest.fixture() def assertResponse(self, device, **attrs): return makeAssertResponse(device, **attrs) async it "responds to label messages", device, assertResponse: await assertResponse(DeviceMessages.GetLabel(), [DeviceMessages.StateLabel(label="")]) await assertResponse( DeviceMessages.SetLabel(label="sam"), [DeviceMessages.StateLabel(label="sam")], label="sam", ) await assertResponse( DeviceMessages.GetLabel(), [DeviceMessages.StateLabel(label="sam")], label="sam" ) async it "responds to power messages", device, assertResponse: await assertResponse(DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=0)]) await assertResponse( DeviceMessages.SetPower(level=200), [DeviceMessages.StatePower(level=0)], power=200 ) await assertResponse(
assert info.completed is None assert not info.done it "can be marked done", V: info = PlanInfo(V.plan, V.plankey, V.instance, None) assert not info.done info.mark_done() assert info.done describe "messages": it "memoizes the messages and cares about instance messages before plan messages": called = [] get_power = DeviceMessages.GetPower() get_label = DeviceMessages.GetLabel() class P(Plan): @property def messages(s): called.append("shouldn't be called") return [get_label] class Instance(Plan.Instance): @property def messages(s): called.append(1) return [get_power] plan = P() instance = plan.Instance("d073d5000001", plan, {})