From fa438c826668c96678fc3b9713e93628eac3761f Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Sat, 18 Dec 2021 19:19:09 -0500 Subject: [PATCH] Move src folder and added ssdp discovery --- .gitignore | 183 +++++++++++++++++++++++ {lib => flickerstrip_py}/__init__.py | 0 flickerstrip_py/discovery.py | 18 +++ {lib => flickerstrip_py}/flickerstrip.py | 0 {lib => flickerstrip_py}/pattern.py | 0 flickerstrip_py/ssdp.py | 66 ++++++++ {lib => flickerstrip_py}/status.py | 0 poetry.lock | 14 +- pyproject.toml | 1 + 9 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 .gitignore rename {lib => flickerstrip_py}/__init__.py (100%) create mode 100644 flickerstrip_py/discovery.py rename {lib => flickerstrip_py}/flickerstrip.py (100%) rename {lib => flickerstrip_py}/pattern.py (100%) create mode 100644 flickerstrip_py/ssdp.py rename {lib => flickerstrip_py}/status.py (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf7cd9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,183 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,python +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,python + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,python + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/lib/__init__.py b/flickerstrip_py/__init__.py similarity index 100% rename from lib/__init__.py rename to flickerstrip_py/__init__.py diff --git a/flickerstrip_py/discovery.py b/flickerstrip_py/discovery.py new file mode 100644 index 0000000..2bba902 --- /dev/null +++ b/flickerstrip_py/discovery.py @@ -0,0 +1,18 @@ +from ssdpy import SSDPClient + + +class FlickerstripDiscoveryClient: + def __init__(self): + self.client = SSDPClient() + + def discover(self): + print("Discovering devices...") + devices = self.client.m_search("ssdp:all") + print(f"Discovered {len(devices)} devices.") + for device in devices: + print(device) + + +if __name__ == "__main__": + client = FlickerstripDiscoveryClient() + client.discover() diff --git a/lib/flickerstrip.py b/flickerstrip_py/flickerstrip.py similarity index 100% rename from lib/flickerstrip.py rename to flickerstrip_py/flickerstrip.py diff --git a/lib/pattern.py b/flickerstrip_py/pattern.py similarity index 100% rename from lib/pattern.py rename to flickerstrip_py/pattern.py diff --git a/flickerstrip_py/ssdp.py b/flickerstrip_py/ssdp.py new file mode 100644 index 0000000..9bfe9bb --- /dev/null +++ b/flickerstrip_py/ssdp.py @@ -0,0 +1,66 @@ +# Copyright 2014 Dan Krause, Python 3 hack 2016 Adam Baxter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import socket +import http.client +import io + + +class SSDPResponse(object): + class _FakeSocket(io.BytesIO): + def makefile(self, *args, **kw): + return self + + def __init__(self, response): + r = http.client.HTTPResponse(self._FakeSocket(response)) + r.begin() + self.location = r.getheader("location") + self.usn = r.getheader("usn") + self.st = r.getheader("st") + self.cache = r.getheader("cache-control").split("=")[1] + + def __repr__(self): + return ""\ + .format(**self.__dict__) + + +def discover(service, timeout=5, retries=1, mx=3): + group = ("239.255.255.250", 1900) + message = "\r\n".join([ + 'M-SEARCH * HTTP/1.1', + 'HOST: {0}:{1}', + 'MAN: "ssdp:discover"', + 'ST: {st}', 'MX: {mx}', '', '']) + socket.setdefaulttimeout(timeout) + responses = {} + for _ in range(retries): + sock = socket.socket( + socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) + message_bytes = message.format( + *group, st=service, mx=mx).encode('utf-8') + sock.sendto(message_bytes, group) + + while True: + try: + response = SSDPResponse(sock.recv(1024)) + responses[response.location] = response + except socket.timeout: + break + return list(responses.values()) + +# Example: +# import ssdp +# ssdp.discover("roku:ecp") diff --git a/lib/status.py b/flickerstrip_py/status.py similarity index 100% rename from lib/status.py rename to flickerstrip_py/status.py diff --git a/poetry.lock b/poetry.lock index 893ba44..0a6e2ab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -205,6 +205,14 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +[[package]] +name = "ssdpy" +version = "0.4.1" +description = "Python SSDP library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<4" + [[package]] name = "toml" version = "0.10.2" @@ -229,7 +237,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "7a9fae5232c9103cdf55efa82df2a2e366af9c25bc9e95144193e5d33674b486" +content-hash = "3a12befea5c0f2cdf0b7779be1a2af0ff1d396566df3dfc771bf2e93c5f83c38" [metadata.files] atomicwrites = [ @@ -308,6 +316,10 @@ requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] +ssdpy = [ + {file = "ssdpy-0.4.1-py2.py3-none-any.whl", hash = "sha256:f2a84915140a8df9d9432e35a487c8712fe23158d278f90f72ed5bd49fcf2bb5"}, + {file = "ssdpy-0.4.1.tar.gz", hash = "sha256:4971c6a010f77f147ecdbec9593f2d9187c3fb63658b0f5ec08f4c7be2387425"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/pyproject.toml b/pyproject.toml index 8f2f6c0..ab984a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ authors = ["Evan Fiordeliso "] [tool.poetry.dependencies] python = "^3.9" requests = "^2.26.0" +ssdpy = "^0.4.1" [tool.poetry.dev-dependencies] pytest = "^6.2.5"