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 respond(s, event): if event | DeviceMessages.GetPower: event.set_replies(DeviceMessages.StatePower(level=s.device_attrs.power)) elif event | DeviceMessages.SetPower: event.set_replies(DeviceMessages.StatePower(level=s.device_attrs.power)) await s.device_attrs.attrs_apply( s.device_attrs.attrs_path("power").changer_to(event.pkt.level), event=event )
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(sd, reference, **kwargs): assert await ( yield DeviceMessages.EchoRequest(echoing=b"hi", target=V.device.serial) ) assert not await ( yield DeviceMessages.EchoRequest(echoing=b"hi", target=V.device2.serial) ) for i in range(5): assert await ( yield DeviceMessages.EchoRequest(echoing=b"hi", target=V.device.serial) )
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 respond(self, device, pkt, source): if pkt | DeviceMessages.GetGroup: yield DeviceMessages.StateGroup( group=device.attrs.group.uuid, label=device.attrs.group.label, updated_at=device.attrs.group.updated_at, ) elif pkt | DeviceMessages.GetLocation: yield DeviceMessages.StateLocation( location=device.attrs.location.uuid, label=device.attrs.location.label, updated_at=device.attrs.location.updated_at, )
async def respond(s, event): if event | DeviceMessages.GetPower: event.set_replies(DeviceMessages.StatePower(level=s.device_attrs.power)) if event | DeviceMessages.GetLabel: event.ignore_request() elif event | DeviceMessages.SetPower: event.set_replies(DeviceMessages.StatePower(level=s.device_attrs.power)) await s.device_attrs.attrs_apply( s.device_attrs.attrs_path("power").changer_to(event.pkt.level), event=None, ) elif event | DiscoveryMessages.GetService: event.set_replies( DiscoveryMessages.StateService(service=Services.UDP, port=56700), DiscoveryMessages.StateService(service=Services.UDP, port=76500), )
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})
def setup(self): self.log_args = (PhotonsAppError("stuff happens", one=1),) self.log_kwargs = { "pkt": DeviceMessages.SetPower(level=65535), "other": [1, 2], "more": True, }
async def start(self, serial, info, target, afr): errors = [] plans = ["chain"] if info.get("initial") is None: info["initial"] = {"colors": [], "power": 65535} plans.append("colors") plans.append("power") plans = make_plans(*plans) gatherer = Gatherer(target) got = await gatherer.gather_all(plans, serial, afr, error_catcher=errors) if errors: return errors, info chain = got[serial][1]["chain"] power = got[serial][1].get("power") colors = got[serial][1].get("colors") if power is not None: info["initial"]["power"] = power["level"] if colors is not None: info["initial"]["colors"] = colors pixel_coords = user_coords_to_pixel_coords(chain["coords_and_sizes"]) info["coords"] = [top_left for top_left, _ in pixel_coords] info["reorient"] = chain["reorient"] if info.get("pixels") is None: canvas = Canvas() def dcf(i, j): return Color(0, 0, 0, 3500) canvas.default_color_func = dcf length = len(info["coords"]) self.style_maker.set_canvas(canvas, length) info["pixels"], info["color_pixels"] = make_rgb_and_color_pixels( canvas, length) msgs = [DeviceMessages.SetPower(level=65535)] msgs.extend( canvas_to_msgs( canvas, coords_for_horizontal_line, duration=1, acks=True, reorient=info["reorient"], )) await target.script(msgs).run_with_all(serial, afr, error_catcher=errors) return errors, info
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)
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 __anext__(self): self.index += 1 if self.index == 0: return ( DeviceMessages.StatePower(level=0, target="d073d5000001"), ("192.168.0.1", 56700), "192.168.0.1", ) elif self.index == 1: return ( DeviceMessages.StateHostFirmware( build=0, version_major=1, version_minor=2, target="d073d5000002" ), ("`92.168.0.2", 56700), "192.168.0.2", ) else: raise StopAsyncIteration
def power_message(self, state): power_level = 65535 if state["power"] == "on" else 0 if state.get("duration") in (sb.NotSpecified, "", 0, None): return DeviceMessages.SetPower(level=power_level, res_required=False) else: return LightMessages.SetLightPower(level=power_level, duration=state["duration"], res_required=False)
def make_response(self, pkt, protocol): self.received.append(pkt) if pkt | LightMessages.GetInfrared: return LightMessages.StateInfrared(brightness=self.infrared) if pkt | LightMessages.GetColor: return LightMessages.LightState( hue = self.hue , saturation = self.saturation , brightness = self.brightness , kelvin = self.kelvin , label = self.label , power = self.power ) elif pkt | DeviceMessages.GetVersion: return DeviceMessages.StateVersion( vendor = self.vendor_id , product = self.product_id , version = 0 ) elif pkt | DeviceMessages.GetHostFirmware: return DeviceMessages.StateHostFirmware( version = self.firmware_version , build = self.firmware_build_time ) elif pkt | DeviceMessages.GetGroup: return DeviceMessages.StateGroup( group = self.group , label = self.group_label , updated_at = self.group_updated_at ) elif pkt | DeviceMessages.GetLocation: return DeviceMessages.StateLocation( location = self.location , label = self.location_label , updated_at = self.location_updated_at )
async def __anext__(self): self.index += 1 if self.index == 0: return ( DeviceMessages.StatePower(level=0, target="d073d5000001"), ("192.168.0.1", 56700), "192.168.0.1", ) elif self.index == 1: self.kwargs["error_catcher"](PhotonsAppError("failure", serial="d073d5000002")) raise StopAsyncIteration
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 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 write(s, transport, bts, original_message): called.append("write") pkt = Messages.unpack(bts, protocol_register=protocol_register) res = DeviceMessages.EchoResponse( source=pkt.source, sequence=pkt.sequence, target=pkt.target, echoing=b"pong" ) loop = asyncio.get_event_loop() bts = res.pack().tobytes() addr = ("fake://device", 56700) loop.call_soon(s.session.sync_received_data, bts, addr)
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
class AddressPlan(Plan): """ Return the ``(ip, port)`` for this device """ messages = [DeviceMessages.EchoRequest(echoing=b"get_remote_addr")] class Instance(Plan.Instance): def process(self, pkt): self.address = pkt.Information.remote_addr return True async def info(self): return self.address
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
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)
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']}")
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 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())
async def respond(self, event): if event | DeviceMessages.GetLabel: event.add_replies(self.state_for(DeviceMessages.StateLabel)) elif event | DeviceMessages.GetPower: event.add_replies(self.state_for(DeviceMessages.StatePower)) elif event | DeviceMessages.SetLabel: await self.change_one("label", event.pkt.label, event=event) event.add_replies(self.state_for(DeviceMessages.StateLabel)) elif event | DeviceMessages.SetPower: event.add_replies(self.state_for(DeviceMessages.StatePower)) await self.change_one("power", event.pkt.level, event=event) elif event | DeviceMessages.EchoRequest: event.add_replies( DeviceMessages.EchoResponse(echoing=event.pkt.echoing))
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 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 execute(self): fltr = chp.filter_from_matcher(self.matcher, self.refresh) result = chp.ResultBuilder() afr = await self.finder.args_for_run() reference = self.finder.find(filtr=fltr) serials = await tile_serials_from_reference(self.target, reference, afr) if not serials: raise FoundNoDevices("Didn't find any tiles") await self.target.script(DeviceMessages.SetPower(level=65535) ).run_with_all(serials, afr, error_catcher=result.error) result.result["results"]["tiles"] = await tile_dice( self.target, serials, afr, error_catcher=result.error) return result