diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..e21abf7 --- /dev/null +++ b/flake.lock @@ -0,0 +1,129 @@ +{ + "nodes": { + "dream2nix": { + "inputs": { + "nixpkgs": "nixpkgs", + "purescript-overlay": "purescript-overlay", + "pyproject-nix": "pyproject-nix" + }, + "locked": { + "lastModified": 1732113111, + "narHash": "sha256-KgGKWOEbqP15O2J6kue4JShHDk5yGG5e1GfY22bjuZU=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "91bec8a0854abfa581a40b5030cfa8f98d2f8ee5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1729850857, + "narHash": "sha256-WvLXzNNnnw+qpFOmgaM3JUlNEH+T4s22b5i2oyyCpXE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "41dea55321e5a999b17033296ac05fe8a8b5a257", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "purescript-overlay": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ], + "slimlock": "slimlock" + }, + "locked": { + "lastModified": 1728546539, + "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, + "pyproject-nix": { + "flake": false, + "locked": { + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", + "repo": "pyproject.nix", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "type": "github" + }, + "original": { + "owner": "davhau", + "ref": "dream2nix", + "repo": "pyproject.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "dream2nix": "dream2nix", + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ] + } + }, + "slimlock": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "purescript-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688756706, + "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", + "owner": "thomashoneyman", + "repo": "slimlock", + "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "slimlock", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..b8b8ed4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,34 @@ +{ + inputs = { + dream2nix.url = "github:nix-community/dream2nix"; + nixpkgs.follows = "dream2nix/nixpkgs"; + }; + + outputs = { + self, + dream2nix, + nixpkgs, + }: let + eachSystem = nixpkgs.lib.genAttrs [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + in { + packages = eachSystem (system: rec { + hcpy = default; + default = dream2nix.lib.evalModules { + packageSets.nixpkgs = nixpkgs.legacyPackages.${system}; + modules = [ + ./package.nix + { + paths.projectRootFile = "flake.nix"; + paths.projectRoot = ./.; + paths.package = ./.; + } + ]; + }; + }); + }; +} diff --git a/lock.x86_64-linux.json b/lock.x86_64-linux.json new file mode 100644 index 0000000..19573f3 --- /dev/null +++ b/lock.x86_64-linux.json @@ -0,0 +1,159 @@ +{ + "fetchPipMetadata": { + "sources": { + "beautifulsoup4": { + "is_direct": false, + "sha256": "b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", + "type": "url", + "url": "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", + "version": "4.12.3" + }, + "bs4": { + "is_direct": false, + "sha256": "abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc", + "type": "url", + "url": "https://files.pythonhosted.org/packages/51/bb/bf7aab772a159614954d84aa832c129624ba6c32faa559dfb200a534e50b/bs4-0.0.2-py2.py3-none-any.whl", + "version": "0.0.2" + }, + "certifi": { + "is_direct": false, + "sha256": "922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "type": "url", + "url": "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", + "version": "2024.8.30" + }, + "charset-normalizer": { + "is_direct": false, + "sha256": "8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "type": "url", + "url": "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "version": "3.4.0" + }, + "click": { + "is_direct": false, + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "url", + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "version": "8.1.7" + }, + "click-config-file": { + "is_direct": false, + "sha256": "3c5802dec437ed596f181efc988f62b1069cd48a912e280cd840ee70580f39d7", + "type": "url", + "url": "https://files.pythonhosted.org/packages/28/16/c71980d10b75cf4ee2c71bb946c3326a18585254399aac64a5e79cfba5a5/click_config_file-0.6.0-py2.py3-none-any.whl", + "version": "0.6.0" + }, + "configobj": { + "is_direct": false, + "sha256": "03c881bbf23aa07bccf1b837005975993c4ab4427ba57f959afdd9d1a2386848", + "type": "url", + "url": "https://files.pythonhosted.org/packages/f5/c4/c7f9e41bc2e5f8eeae4a08a01c91b2aea3dfab40a3e14b25e87e7db8d501/configobj-5.0.9.tar.gz", + "version": "5.0.9" + }, + "idna": { + "is_direct": false, + "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", + "type": "url", + "url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", + "version": "3.10" + }, + "lxml": { + "is_direct": false, + "sha256": "3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", + "type": "url", + "url": "https://files.pythonhosted.org/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", + "version": "5.3.0" + }, + "paho-mqtt": { + "is_direct": false, + "sha256": "2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f", + "type": "url", + "url": "https://files.pythonhosted.org/packages/f8/dd/4b75dcba025f8647bc9862ac17299e0d7d12d3beadbf026d8c8d74215c12/paho-mqtt-1.6.1.tar.gz", + "version": "1.6.1" + }, + "pycryptodome": { + "is_direct": false, + "sha256": "0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8", + "type": "url", + "url": "https://files.pythonhosted.org/packages/ea/66/6f2b7ddb457b19f73b82053ecc83ba768680609d56dd457dbc7e902c41aa/pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "version": "3.21.0" + }, + "requests": { + "is_direct": false, + "sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", + "type": "url", + "url": "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", + "version": "2.32.3" + }, + "setuptools": { + "is_direct": false, + "sha256": "ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", + "type": "url", + "url": "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", + "version": "75.6.0" + }, + "soupsieve": { + "is_direct": false, + "sha256": "e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", + "type": "url", + "url": "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", + "version": "2.6" + }, + "sslpsk": { + "is_direct": false, + "sha256": "3472276c0dde76d2f3831b8291f899106fe53947c4dc6dca053517f1c767a91d", + "type": "url", + "url": "https://files.pythonhosted.org/packages/87/1c/b8b5a2d0af9f9a3624d65ce1981777275ac765be45839c4c052018ec715e/sslpsk-1.0.0.tar.gz", + "version": "1.0.0" + }, + "urllib3": { + "is_direct": false, + "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "type": "url", + "url": "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", + "version": "2.2.3" + }, + "websocket-client": { + "is_direct": false, + "sha256": "17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", + "type": "url", + "url": "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", + "version": "1.8.0" + } + }, + "targets": { + "default": { + "beautifulsoup4": [ + "soupsieve" + ], + "bs4": [ + "beautifulsoup4" + ], + "certifi": [], + "charset-normalizer": [], + "click": [], + "click-config-file": [ + "click", + "configobj" + ], + "configobj": [], + "idna": [], + "lxml": [], + "paho-mqtt": [], + "pycryptodome": [], + "requests": [ + "certifi", + "charset-normalizer", + "idna", + "urllib3" + ], + "setuptools": [], + "soupsieve": [], + "sslpsk": [], + "urllib3": [], + "websocket-client": [] + } + } + }, + "invalidationHash": "8b77dd84722cef2a9d624d9674be2397c777c8b180b813df1327f67470dd3476" +} \ No newline at end of file diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..2802664 --- /dev/null +++ b/package.nix @@ -0,0 +1,50 @@ +{ + config, + lib, + dream2nix, + pkgs, + ... +}: let + pyproject = lib.importTOML (config.mkDerivation.src + /pyproject.toml); +in rec { + imports = [ + dream2nix.modules.dream2nix.pip + ]; + + inherit (pyproject.project) name version; + + deps = {nixpkgs, ...}: { + python = nixpkgs.python3; + inherit (nixpkgs) openssl; + }; + + mkDerivation = { + src = lib.cleanSourceWith { + src = lib.cleanSource ./.; + filter = name: type: + !(builtins.any (x: x) [ + (lib.hasSuffix ".nix" name) + (lib.hasPrefix "." (builtins.baseNameOf name)) + (lib.hasSuffix "flake.lock" name) + ]); + }; + }; + + buildPythonPackage = { + pyproject = true; # + pythonImportsCheck = [ + "hcpy" + ]; + }; + + pip = { + # requirementsList = lib.splitString "\n" (builtins.readFile (config.mkDerivation.src + /requirements.txt)); + requirementsList = + pyproject.build-system.requires + or [] + ++ pyproject.project.dependencies or []; + flattenDependencies = true; + + overrides.sslpsk.mkDerivation.buildInputs = [config.deps.openssl]; + }; +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e50a7c7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "hcpy" +version = "0.1.5" +description = "HC to MQTT bridge" +requires-python = ">=3.7" +dependencies = [ + "bs4", + "requests", + "pycryptodome", + "websocket-client", + "sslpsk", + "paho-mqtt==1.6.1", + "lxml", + "click", + "click-config-file" +] + +[project.scripts] +hc-login = "hcpy:hc_login" +hc2mqtt = "hcpy.hc2mqtt:hc2mqtt" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 493ebbc..0000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -bs4 -requests -pycryptodome -websocket-client -sslpsk -paho.mqtt==1.6.1 -lxml -click -click-config-file -requests diff --git a/HADiscovery.py b/src/hcpy/HADiscovery.py similarity index 99% rename from HADiscovery.py rename to src/hcpy/HADiscovery.py index e1ee8ab..f58b084 100644 --- a/HADiscovery.py +++ b/src/hcpy/HADiscovery.py @@ -1,7 +1,7 @@ import json import re -from HCSocket import now +from hcpy.HCSocket import now def decamelcase(str): diff --git a/HCDevice.py b/src/hcpy/HCDevice.py similarity index 100% rename from HCDevice.py rename to src/hcpy/HCDevice.py diff --git a/HCSocket.py b/src/hcpy/HCSocket.py similarity index 100% rename from HCSocket.py rename to src/hcpy/HCSocket.py diff --git a/HCxml2json.py b/src/hcpy/HCxml2json.py similarity index 100% rename from HCxml2json.py rename to src/hcpy/HCxml2json.py diff --git a/hc2mqtt.py b/src/hcpy/hc2mqtt.py similarity index 98% rename from hc2mqtt.py rename to src/hcpy/hc2mqtt.py index 669245c..148ee95 100755 --- a/hc2mqtt.py +++ b/src/hcpy/hc2mqtt.py @@ -11,9 +11,9 @@ import click import click_config_file import paho.mqtt.client as mqtt -from HADiscovery import publish_ha_discovery -from HCDevice import HCDevice -from HCSocket import HCSocket, now +from hcpy.HADiscovery import publish_ha_discovery +from hcpy.HCDevice import HCDevice +from hcpy.HCSocket import HCSocket, now @click.command() diff --git a/hc-login.py b/src/hcpy/hc_login.py similarity index 99% rename from hc-login.py rename to src/hcpy/hc_login.py index 9bcd3d8..4934a31 100755 --- a/hc-login.py +++ b/src/hcpy/hc_login.py @@ -16,8 +16,8 @@ from bs4 import BeautifulSoup from Crypto.Hash import SHA256 from Crypto.Random import get_random_bytes -from HADiscovery import augment_device_features -from HCxml2json import xml2json +from hcpy.HADiscovery import augment_device_features +from hcpy.HCxml2json import xml2json # These two lines enable debugging at httplib level (requests->urllib3->http.client) # You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.