diff --git a/.gitignore b/.gitignore index d588798..ba84190 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /serve/config.txt /logs +.env diff --git a/nix/lon.lock b/nix/lon.lock new file mode 100644 index 0000000..93f39b6 --- /dev/null +++ b/nix/lon.lock @@ -0,0 +1,15 @@ +{ + "version": "1", + "sources": { + "nixpkgs": { + "type": "GitHub", + "fetchType": "tarball", + "owner": "NixOS", + "repo": "nixpkgs", + "branch": "nixos-unstable", + "revision": "d6c71932130818840fc8fe9509cf50be8c64634f", + "url": "https://github.com/NixOS/nixpkgs/archive/d6c71932130818840fc8fe9509cf50be8c64634f.tar.gz", + "hash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=" + } + } +} diff --git a/nix/lon.nix b/nix/lon.nix new file mode 100644 index 0000000..a88d448 --- /dev/null +++ b/nix/lon.nix @@ -0,0 +1,53 @@ +# Generated by lon. Do not modify! +let + + lock = builtins.fromJSON (builtins.readFile ./lon.lock); + + # Override with a path defined in an environment variable. If no variable is + # set, the original path is used. + overrideFromEnv = + name: path: + let + replacement = builtins.getEnv "LON_OVERRIDE_${name}"; + in + if replacement == "" then + path + else + # this turns the string into an actual Nix path (for both absolute and + # relative paths) + if builtins.substring 0 1 replacement == "/" then + /. + replacement + else + /. + builtins.getEnv "PWD" + "/${replacement}"; + + fetchSource = + args@{ fetchType, ... }: + if fetchType == "git" then + builtins.fetchGit ( + { + url = args.url; + ref = args.branch; + rev = args.revision; + narHash = args.hash; + submodules = args.submodules; + } + // ( + if args ? lastModified then + { + inherit (args) lastModified; + shallow = true; + } + else + { } + ) + ) + else if fetchType == "tarball" then + builtins.fetchTarball { + url = args.url; + sha256 = args.hash; + } + else + builtins.throw "Unsupported source type ${fetchType}"; + +in +builtins.mapAttrs (name: args: overrideFromEnv name (fetchSource args)) lock.sources diff --git a/nix/tapo.nix b/nix/tapo.nix new file mode 100644 index 0000000..062c809 --- /dev/null +++ b/nix/tapo.nix @@ -0,0 +1,46 @@ +{ + lib, + rustPlatform, + buildPythonPackage, + maturin, + fetchFromGitHub, +}: + +buildPythonPackage rec { + pname = "tapo"; + version = "0.8.8"; + pyproject = true; + + src = fetchFromGitHub { + owner = "mihai-dinculescu"; + repo = "tapo"; + rev = "v${version}"; + hash = "sha256-cVrD2XfS3Oum0DL9DYQfnNN6WKyjSiy94Vj+sUMmbcc="; + }; + + nativeBuildInputs = [ + rustPlatform.cargoSetupHook + rustPlatform.maturinBuildHook + ]; + + sourceRoot = "${src.name}/tapo-py"; + cargoRoot = "../"; + + env = { + CARGO_TARGET_DIR = "./target"; + }; + + cargoDeps = rustPlatform.fetchCargoVendor { + inherit pname version src; + hash = "sha256-Klr3rx1zYcul1gPz6H+d5idHlNJQB3SuY5nZlXwHFmY="; + }; + + meta = { + description = "Unofficial Tapo API Client. Works with TP-Link Tapo smart devices. Tested with light bulbs (L510, L520, L530, L535, L610, L630), light strips (L900, L920, L930), plugs (P100, P105, P110, P110M, P115), power strips (P300, P304M, P316M), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315"; + homepage = "https://github.com/mihai-dinculescu/tapo"; + changelog = "https://github.com/mihai-dinculescu/tapo/blob/${src.rev}/CHANGELOG.md"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ ]; + mainProgram = "tapo"; + }; +} diff --git a/powercut.py b/powercut.py new file mode 100644 index 0000000..4400876 --- /dev/null +++ b/powercut.py @@ -0,0 +1,47 @@ +import asyncio +import os +import shutil +import sys +import time +from datetime import datetime +from pathlib import Path + +import tapo +from dotenv import load_dotenv +from scapy.all import ICMP, IP, sr + +load_dotenv() + +tapo_username = os.getenv("TAPO_USERNAME") +tapo_password = os.getenv("TAPO_PASSWORD") +tapoip = os.getenv("TAPO_IP") + + +async def powercycle(): + tapoapi = tapo.ApiClient(tapo_username, tapo_password) + tapoplug = await tapoapi.p100(tapoip) + await tapoplug.off() + await asyncio.sleep(1) + await tapoplug.on() + + +asyncio.run(powercycle()) + +print("sleeping for 80s, then ping until up") + +time.sleep(80) + +while True: + ans, unans = sr(IP(dst="192.168.42.6") / ICMP(), verbose=False) + if len(ans) > 0: + break + +print("Collecting :") + +timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + "-powercut" +log_dir = Path("./logs") / timestamp +log_dir.mkdir(parents=True, exist_ok=True) + +shutil.copy(Path(sys.argv[0]), log_dir / sys.argv[0]) + +print(" - [x] script.py") diff --git a/powercut.sh b/powercut.sh deleted file mode 100755 index 3650a42..0000000 --- a/powercut.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -echo "sleeping for 80s, then ping until up" - -sleep 80 - -while true; -do - if ping -w 1 -c 1 192.168.42.6 > /dev/null; then - break - fi -done - -echo "collecting logs" - -dir="./logs/$(date +"%Y%m%d_%H%M%S")-powercut" - -mkdir -p "$dir" - -touch "$dir/powercut" - -cp ./powercut.sh "$dir" - -echo "finished" diff --git a/shell.nix b/shell.nix index 6f95095..0c94da5 100644 --- a/shell.nix +++ b/shell.nix @@ -1,10 +1,13 @@ -{ pkgs ? import {} }: +{ pkgs ? import (import ./nix/lon.nix).nixpkgs {} }: pkgs.mkShell { name = "junos-testing"; packages = [ pkgs.d2 + pkgs.lon (pkgs.python3.withPackages (p: [ + (p.callPackage ./nix/tapo.nix {}) p.scapy + p.python-dotenv p.tqdm p.ipython p.libpcap