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})
async def gen(reference, sender, **kwargs): get_power = DeviceMessages.GetPower() async for pkt in sender(get_power, reference, **kwargs): if pkt | DeviceMessages.StatePower: if pkt.level == 0: yield DeviceMessages.SetPower(level=65535, target=pkt.serial) else: yield DeviceMessages.SetPower(level=0, target=pkt.serial)
async def gen(reference, sender, **kwargs): get_power = DeviceMessages.GetPower() async for pkt in sender(get_power, reference, **kwargs): if pkt | DeviceMessages.StatePower: if pkt.level == 0: yield LightMessages.SetLightPower(level=65535, res_required=False, duration=duration, target=pkt.serial) else: yield LightMessages.SetLightPower(level=0, res_required=False, duration=duration, target=pkt.serial)
class PowerPlan(Plan): """ Return in a dictionary: * level: The current power level on the device * on: ``False`` if the level is 0, otherwise ``True`` """ messages = [DeviceMessages.GetPower()] default_refresh = 1 class Instance(Plan.Instance): def process(self, pkt): if pkt | DeviceMessages.StatePower: self.level = pkt.level self.on = pkt.level > 0 return True async def info(self): return {"level": self.level, "on": self.on}
async def gen(reference, sender, **kwargs): async def inner_gen(level, reference, sender2, **kwargs2): assert sender is sender2 del kwargs2["error_catcher"] kwargs1 = dict(kwargs) del kwargs1["error_catcher"] assert kwargs1 == kwargs2 assert reference in devices.serials yield DeviceMessages.SetPower(level=level) get_power = DeviceMessages.GetPower() async for pkt in sender(get_power, reference, **kwargs): if pkt.serial == light1.serial: level = 1 elif pkt.serial == light2.serial: level = 2 elif pkt.serial == light3.serial: level = 3 else: assert False, f"Unknown serial: {pkt.serial}" yield FromGenerator(partial(inner_gen, level), reference_override=pkt.serial)
async def gen(serial, sender, **kwargs): yield Pipeline([DeviceMessages.GetPower(), DeviceMessages.SetLabel(label="wat")])
for device, msgs in expected.items(): assert device in devices devices.store(device).assertIncoming(*msgs, ignore=[DiscoveryMessages.GetService]) devices.store(device).clear() async it "is able to do a FromGenerator per serial", sender: async def gen(serial, sender, **kwargs): assert serial in (light1.serial, light2.serial) yield Pipeline([DeviceMessages.GetPower(), DeviceMessages.SetLabel(label="wat")]) msg = FromGeneratorPerSerial(gen) expected = { light1: [DeviceMessages.GetPower(), DeviceMessages.SetLabel(label="wat")], light2: [DeviceMessages.GetPower(), DeviceMessages.SetLabel(label="wat")], light3: [], } errors = [] got = defaultdict(list) try: async with light3.offline(): async for pkt in sender(msg, devices.serials, error_catcher=errors): got[pkt.serial].append(pkt) finally: assert errors == [FailedToFindDevice(serial=light3.serial)] assert len(devices) > 0
"line2", "explosion = True", "help_us = please", ) describe "lines_from_a_packet": def assertLines(self, pkt, *lines): assertLines(ConsoleFormat.lines_from_packet, pkt, *lines) it "can format acknowledgements": ack = CoreMessages.Acknowledgement(source=20, sequence=2, target="d073d5001111") self.assertLines(ack, f"Ack(source={ack.source},sequence=2,target=d073d5001111)(empty)") it "can format simple messages": pkt = DeviceMessages.GetPower(source=21, sequence=3, target="d073d5002222") self.assertLines( pkt, f"GetPower(ack=True,res=True,source={pkt.source},sequence=3,target=d073d5002222)(empty)", ) it "can format messages with fields": pkt = LightMessages.SetColor( res_required=False, source=22, sequence=4, target="d073d5003333", hue=200, saturation=1, brightness=0.5, kelvin=9000,
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( DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=200)], power=200 ) describe "SwitchDevice": @pytest.fixture() def device(self): device = devices["switch"] devices.store(device).assertAttrs(label="") return device
async def gen(serial, sender, **kwargs): assert serial in (light1.serial, light2.serial) yield Pipeline([DeviceMessages.GetPower(), DeviceMessages.SetLabel(label="wat")])
async def gen(reference, sender, **kwargs): assert await (yield DeviceMessages.GetPower(target=light1.serial)) assert not await (yield DeviceMessages.GetPower(target=light2.serial)) assert await (yield DeviceMessages.GetPower(target=light3.serial))
async def gen(reference, sender, **kwargs): t = yield DeviceMessages.GetPower() assert not (await t)
yield device @pytest.fixture() async def sender(self, final_future, device): configuration = {"final_future": final_future, "protocol_register": protocol_register} async with MemoryTarget.create(configuration, {"devices": [device]}).session() as sender: yield sender async it "can send and receive messages using memory target", sender, device: pkts = await sender(DeviceMessages.SetPower(level=65535), device.serial) assert len(pkts) == 1 pkt = pkts[0] assert pkt | DeviceMessages.StatePower assert pkt.level == 0 pkts = await sender(DeviceMessages.GetPower(), device.serial) assert len(pkts) == 1 pkt = pkts[0] assert pkt | DeviceMessages.StatePower assert pkt.level == 65535 pkts = await sender(DeviceMessages.SetPower(level=0, res_required=False), device.serial) assert len(pkts) == 0 pkts = await sender(DeviceMessages.GetPower(), device.serial) assert len(pkts) == 1 pkt = pkts[0] assert pkt | DeviceMessages.StatePower assert pkt.level == 0 async it "times out if the device is offline", sender, device, FakeTime, MockedCallLater:
def incoming_event(device, io): return Events.INCOMING( device, io, pkt=DeviceMessages.GetPower(source=20, sequence=34, target=device.serial) )
def item(): return Item([DeviceMessages.GetPower(), DeviceMessages.GetLabel()])
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( DeviceMessages.SetPower(level=200), [DeviceMessages.StatePower(level=0)], power=200 ) await device.assertResponse( DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=200)], power=200 ) async it "responds to light power messages", device: await device.assertResponse( DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=0)] ) await device.assertResponse( LightMessages.SetLightPower(level=200), [LightMessages.StateLightPower(level=0)],
async def gen(reference, sender, **kwargs): raise error yield DeviceMessages.GetPower()
@pytest.fixture() def assertResponse(self, device, **attrs): return makeAssertResponse(device, **attrs) @pytest.fixture() def assertState(self, device, **attrs): return makeAssertState(device, **attrs) @pytest.fixture() def assertEvent(self, device, **attrs): return makeAssertEvent(device, **attrs) async it "can change the power of all the relays", device, assertResponse, assertEvent: await assertResponse( DeviceMessages.GetPower(), [DeviceMessages.StatePower(level=0)], ) await assertResponse( DeviceMessages.SetPower(level=65535), [DeviceMessages.StatePower(level=0)], relays=[ Relay.create(power=65535), Relay.create(power=65535), Relay.create(power=65535), Relay.create(power=65535), ], power=65535, )
io_source = "TEST" async def _send_reply(s, send, give_reply, addr, *, replying_to): got.append(("sent", send, give_reply, addr, replying_to)) addr = mock.Mock(name="addr") give_reply = mock.Mock(name="give_reply") given1 = mock.Mock(name="given1") given2 = mock.Mock(name="given2") filtered1 = mock.Mock(name="filtered1") filtered2 = mock.Mock(name="filtered2") filtered3 = mock.Mock(name="filtered3") async with wrap_io(MyIO(device)) as io: pkt = DeviceMessages.GetPower() event = Events.INCOMING(device, io, pkt=pkt, addr=addr) class Instruction: def __init__(s): s.event = event async def process(s): got.append(("process", given1)) yield given1 got.append(("process", given2)) yield given2 instruction = Instruction() async def outgoing(r, e):
async def run_and_compare(self, runner, msg, *, expected): await runner.sender(msg, runner.serials) assert len(runner.devices) > 0 for device in runner.devices: if device not in expected: assert False, f"No expectation for {device.serial}" device.compare_received(expected[device]) async it "toggles the power", runner: expected = { light1: [ DeviceMessages.GetPower(), LightMessages.SetLightPower(level=65535, duration=1), ], light2: [DeviceMessages.GetPower(), LightMessages.SetLightPower(level=0, duration=1)], light3: [DeviceMessages.GetPower(), LightMessages.SetLightPower(level=0, duration=1)], } await self.run_and_compare(runner, PowerToggle(), expected=expected) for device in runner.devices: device.received = [] expected = { light1: [DeviceMessages.GetPower(), LightMessages.SetLightPower(level=0, duration=2)], light2: [ DeviceMessages.GetPower(), LightMessages.SetLightPower(level=65535, duration=2),
async def gen(reference, sender, **kwargs): yield FailedToFindDevice(serial=light1.serial) yield DeviceMessages.GetPower(target=light2.serial) yield DeviceMessages.GetPower(target=light3.serial)
async def gather(self, sender, reference, *by_label, **kwargs): plan_args = [] plan_kwargs = {} for thing in by_label: if isinstance(thing, str): plan_args.append(thing) else: plan_kwargs.update(thing) plans = sender.make_plans(*plan_args, **plan_kwargs) return dict(await sender.gatherer.gather_all(plans, reference, **kwargs)) describe "PacketPlan": async it "gets the packet", sender: plan = PacketPlan(DeviceMessages.GetPower(), DeviceMessages.StatePower) got = await self.gather(sender, two_lights, {"result": plan}) assert got == { light1.serial: (True, {"result": mock.ANY}), light2.serial: (True, {"result": mock.ANY}), } pytest.helpers.print_packet_difference( got[light1.serial][1]["result"], DeviceMessages.StatePower(level=0) ) pytest.helpers.print_packet_difference( got[light2.serial][1]["result"], DeviceMessages.StatePower(level=65535) ) async it "fails if we can't get the correct response", sender: plan = PacketPlan(DeviceMessages.GetPower(), DeviceMessages.StateLabel)
describe "retry_gaps": async it "returns the retry gaps", V: kwargs = {"host": "192.168.0.3", "port": 56700} transport = await V.session.make_transport("d073d5", Services.UDP, kwargs) assert isinstance(transport, UDP) packet = mock.NonCallableMock(name="packet", spec=[]) uro1 = V.session.retry_gaps(packet, transport) assert uro1 is V.transport_target.gaps describe "determine_needed_transport": async it "says udp", V: services = mock.NonCallableMock(name="services", spec=[]) packets = [DeviceMessages.GetPower(), DeviceMessages.StateLabel()] for packet in packets: got = await V.session.determine_needed_transport(packet, services) assert got == [Services.UDP] describe "choose_transport": async it "complains if we can't determined need transport", V: determine_needed_transport = pytest.helpers.AsyncMock(name="determine_needed_transport") determine_needed_transport.return_value = [] packet = mock.Mock(name="packet", protocol=9001, pkt_type=89) services = mock.Mock(name="services") msg = "Unable to determine what service to send packet to" kwargs = {"protocol": 9001, "pkt_type": 89}
info = PlanInfo(V.plan, V.plankey, V.instance, None) 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()