Categories
arduino Uncategorized

Converting Radiohead ASK Transmissions to ASCII

This is a quick one, mostly because I don’t want to forget it. I’ve been playing with rtl_433 (a super-easy SDR package), and have been piping all of the transmissions received to a local mosquitto server to be used within node-red. I run this command to start receiving:

rtl_433 -F json -M utc | mosquitto_pub -t home/rtl_433 -l -h localhost

Once that’s running, you can add the MQTT node in node-red, using topic “home/rtl_433”. If you’re lucky, you’ll start getting semi-interesting stuff right away (I get inundated with signals from tire pressure systems). I noticed that when I was testing 433Mhz transmissions with the RadioHead library on Arduino, I would get these transmissions:

{"time":"2019-06-13 13:31:52","model":"RadioHead ASK","len":29,"to":255,"from":255,"id":0,"flags":0,"payload":[87,101,108,99,111,109,101,32,116,111,32,116,104,101,32,87,111,114,107,115,104,111,112,32,100,117,100,101,33],"mic":"CRC"}

This was really welcome at the time because I was having a difficult time with the hardware receivers.  The problem is that the payload is received in ASCII, so we need a way to decipher it in node-red, and there is not a pre-made node built to handle this (surprisingly). This snippet within a function node did the trick for me.

var stream = msg.payload.payload;		
msg.payload = String.fromCharCode.apply(null, stream);
return msg;

This converts the array of ASCII values into a readable string.

Welcome to the Workshop!

Now that we’ve got that, we can use the RadioHead library and the cheap 433Mhz transmitters to send something meaningful and have it acted upon within node-red. Thanks to DroneBot Workshop for the transmitter code and thanks to Steve’s Internet Guide for node-red javascript function help.

UPDATE: If you use commas to separate what you’re sending, here’s the code that you want. This examples just has two elements.

var output = msg.payload.split(",");
msg.payload = {
    id :output[0],
    range : output[1] 
}
return msg;

 

Categories
powershell Uncategorized

Powershell: Implementing Automated Screenshots

I needed screenshot functionality for my testing framework. Typically when something fails, I have most of the environment variables to investigate along with the error codes, but a screenshot gives additional context, particularly in my browser automation stuff. There is no native command to do this in Powershell v2.

So, I combined three different scripts I found while trying to sort out screenshot functionality to create some code that:

  1. Brings a existing process’s window to the foreground (can’t take a screenshot of it if we can’t see it obviously).
  2. Calculates the dimensions of the window.
  3. Uses the calculated dimensions to take a screenshot and write it to a file (C:\MyFancyScreenshot.bmp in this example).
$handle = (Get-Process -Name notepad).MainWindowHandle

You will need to narrow this down to make sure you get exactly what you want. It’s going to fail if you have multiple instances of notepad open. Take a look at these links if you’d like additional context.

https://blogs.technet.microsoft.com/heyscriptingguy/2015/12/26/weekend-scripter-manage-window-placement-by-using-pinvoke/
https://www.adminarsenal.com/blog/capturing-screenshots-with-powershell-and-net/
http://stackoverflow.com/questions/2556872/how-to-set-foreground-window-from-powershell-event-subscriber-action

Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class Window {
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    }
 
    public struct RECT
    {
    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner
    }
"@
 
Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks {
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  }
"@
 
$handle = (Get-Process -Name notepad).MainWindowHandle
[void] [Tricks]::SetForegroundWindow($handle)
$Rectangle = New-Object RECT
[Window]::GetWindowRect($Handle,[ref]$Rectangle)
$File = "C:\MyFancyScreenshot.bmp"
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width = $rectangle.right - $rectangle.left
$height = $rectangle.bottom - $rectangle.top
$left = $rectangle.left
$top = $rectangle.top
# Create bitmap using the top-left and bottom-right bounds
$bitmap = New-Object System.Drawing.Bitmap $Width, $Height
# Create Graphics object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)
# Capture screen
$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
# Save to file
$bitmap.Save($File)
Write-Output "Screenshot saved to:"
Write-Output $File
Categories
Uncategorized

Python: Automating Analog Video Capture with pyautogui

Early in December I was talking to a co-worker about how expensive tape-to-digital conversion was. I was charged over $60 locally for less than a couple hours of tape – and when I saw their setup, it was an Elgato Video Capture device. I put it on my Amazon wishlist, and it went on sale (almost immediately after I had the aforementioned conversation) for $50-ish. I bought it, and I’ve been impressed with the results, so I volunteered to do a bunch of conversions (8MM) for my parents.

The end product is good, but the software managing the capture is a little bit clunky and doesn’t “sense” when a tape hits dead air, which means I have to babysit the process or let it take a long time and then trim it back down to size. The process goes like this:

  1. Give your project (video) a name.
  2. Click continue a couple times confirming that the device is receiving video and audio.
  3. Click a big red button to “Start Recording”.
  4. When you hit the point on the tape where there is no more content, you click “Stop Recording”.
  5. You are then given the opportunity to trim the beginning and end of the capture, and click continue.
  6. “Please wait while the movie is being processed.” For a long capture, this can take a half hour.
  7. Done!

So, this is a perfect opportunity to use what I learned in the last chapter of Al Sweigart’s (free) book “Automate the Boring Stuff with Python” regarding automating programs that cannot be manipulated through other means. Al wrote a really useful Python module that uses image recognition to automate, called pyautogui. I have been looking for a good opportunity to use this, because most of the tasks I run into can be broken down into a faster (and more efficient) manipulation type. Elgato’s video capture interface doesn’t let me intervene through an API, so this was perfect.

import pyautogui
import time
import requests
def ifttt_alert():
    requests.post("https://maker.ifttt.com/trigger/YOUR_TRIGGER_NAME/with/key/YOUR_IFTTT_SECRET_KEY")

nosignal = pyautogui.locateOnScreen('no-signal.png', grayscale=True)
while nosignal is None:
    print("Still getting signal.")
    time.sleep(1)
    nosignal = pyautogui.locateCenterOnScreen('no-signal.png', grayscale=True)
    if nosignal is not None:
        print("Looks like the signal went out. Giving it 20 seconds to recover.")
        time.sleep(20)
        nosignal = pyautogui.locateCenterOnScreen('no-signal.png', grayscale=True)
print("Stopping recording.")
stoprecording = pyautogui.locateCenterOnScreen('stoprecording.png', grayscale=True)
pyautogui.click(stoprecording)
time.sleep(10)
continuebutton = pyautogui.locateCenterOnScreen('continue.png', grayscale=True)
pyautogui.click(continuebutton)
pleasewait = pyautogui.locateCenterOnScreen('pleasewait.png', grayscale=True)
while pleasewait is not None:
    time.sleep(10)
    pleasewait = pyautogui.locateCenterOnScreen('pleasewait.png', grayscale=True)
ifttt_alert()
print("Process complete.")

It’s pretty straightforward. The idea is that I have to be there to cue up the tape, so I can manually handle starting the program and hitting play on the camcorder. Then I start this script and it is constantly scraping the screen for an image that looks like this: 
If the script detects that image, then it is going to give the signal 20 seconds to recover. It’s possible that there is just a break on the tape between recordings, and this gives us a chance to see if there is more before we shut down. If 20 seconds do pass, and the image is still found on the screen, we click “Stop Recording”, which looks like this: 

We then give it ten seconds to wrap up it’s process (which is generous, but just in case) and click the “Continue” button. 

Now this is the point where we run into the “Please wait while the movie is being processed” part. So we are going to search the screen every 10 seconds for the following image until it doesn’t exist on the screen anymore.

When it doesn’t exist anymore, the process is finished. As an added bonus, I make an API call to the IFTTT service that will then send a notification to my phone letting me know that it’s time to cue up another tape. The only thing left to do is to rewind the current tape – which I’m actually trying to find out if it’s possible to use an IR blaster to do that!

This was a lot of fun, just had to share.

Categories
Uncategorized

PowerShell: Automatically claiming Free Music from Google Play from Slickdeals Thread

Slickdeals posts these lists of free music from Google Play from time to time. The problem is that the lists can be huge (which is good, shouldn’t be complaining), which means a ton of clicking. This was a spur of the moment script to automate the tedious process. It’s ugly, but it’s going through and adding everything automatically. I’ll take it.

$musiclinks = (Invoke-WebRequest –Uri ‘https://slickdeals.net/f/8889299-free-mp3s-google-play’).Links | Where-Object {$_.InnerText -eq "google.com"}

$ie = (New-Object -COM "Shell.Application").Windows() `
        | ? { $_.Name -eq "Internet Explorer" }


ForEach ($musiclink in $musiclinks)
    {
    $ie.Navigate($musiclink.href)
    while($IE.busy) {Start-Sleep 1}
    sleep 2
    IF(($ie.Document.querySelector(".medium .id-track-impression").IHTMLElement_outerText).trim() -eq "Free")
        {
        $ie.Document.querySelector(".medium span").click()
        sleep 2
        while($IE.busy) {Start-Sleep 1}
        $ie.Document.querySelector("#loonie-purchase-ok-button > span").click()
        while($IE.busy) {Start-Sleep 1}
        sleep 5
        }

    ELSE{
        #$ie.Document.querySelector(".medium .id-no-nav").click()
        write-host "we already got this one."
        }
    }
Categories
Uncategorized

Parsing Football Records from IHSA.org with Python and BeautifulSoup

If you run the attached python code, you’ll output a little over 36k records, every high school football season that the IHSA website has a record for. The IHSA website is fantastic for historical data, but the data is not easily manipulated or downloadable, so I spent some time learning the python library BeautifulSoup to force the issue. Without spending much time playing with this first project in Excel, just a couple things I noticed:

  • There are 2041 seasons listed in the IHSA team record section that do not list a head coach. I’m wondering how difficult it would be to send emails out to each school to see if they could help complete the set.
  • It’s really interesting and easy to select a single coach and have the view of how their total career went, across schools. Pretty labor-intensive before.
    Coal City 1984-85 5 4 Hal Chiodo
    Coal City 1985-86 4 5 Hal Chiodo
    Lexington 1992-93 1A Q 9 3 Hal Chiodo
    Lexington 1993-94 1A Q 6 4 Hal Chiodo
    Lexington 1994-95 1A Q 2 11 3 Hal Chiodo
    Morton 1995-96 4A Q 6 4 Hal Chiodo
    Morton 1996-97 4A Q 7 3 Hal Chiodo
    Morton 1997-98 4A Q 9 3 Hal Chiodo
    Morton 1998-99 4A Q 7 3 Hal Chiodo
    Morton 1999-00 3 6 Hal Chiodo
    Morton 2000-01 4A Q 6 4 Hal Chiodo
    Morton 2001-02 5A Q 5 5 Hal Chiodo
    Morton 2002-03 5A Q 8 2 Hal Chiodo
    Morton 2003-04 5A Q 8 2 Hal Chiodo
    Morton 2004-05 5A Q 6 4 Hal Chiodo
    Morton 2005-06 5A Q 8 3 Hal Chiodo
    Morton 2006-07 4A Q 7 3 Hal Chiodo
    Morton 2007-08 5A Q 5 5 Hal Chiodo
    Highland Park 2009-10 6A Q 7 4 Hal Chiodo
    West Chicago (H.S.) 2009-10 0 9 Hal Chiodo
    Highland Park 2010-11 7A Q 5 5 Hal Chiodo
    Highland Park 2011-12 5 4 Hal Chiodo
    Highland Park 2012-13 3 6 Hal Chiodo
    Highland Park 2013-14 7A Q 7 3 Hal Chiodo
  • The earliest season on file:
    Jacksonville (Illinois School for the Deaf) 1885-86
  • Only two teams have ever won a state championship with four losses. Teams with four losses never made the playoffs before the eight-class era. It’s not surprising that both are private schools.
    Elmhurst (IC Catholic) 2008-09
    Lombard (Montini) 2009-10

Here’s the raw text output of the script if you’d rather just have it directly.

Categories
Uncategorized

Powershell: Fire HTML Form Element Javascript with jQuery

$ie.document.Script.execScript("`$(`"#mytext`").trigger(`"change`");")

If you’re tasked with the automation of a form element, you will find that most of the time this manipulation will not fire javascript code necessary to complete your task. Consider a dynamic dropdown box – you make your selection in the first element, and now that you’ve chosen, the second element is populated. With automation you’ll be stuck trying to figure out how to get that second element populated. The code above (swap out your id element for mytext) will force the associated javascript to fire. It’s embarrassing, but this took forever for me to figure out. I had resorted to emulated keystrokes – but emulated keystrokes don’t allow your automation to continue behind a screensaver.

While I’m on it – why does the below code work? I can’t find documentation on it.

$ie.document.Script."alert".Invoke("Hi")

Categories
Uncategorized

Using Python to make an API call to data.cms.gov

import json,urllib.request
response = urllib.request.urlopen("https://data.cms.gov/resource/qsk8-thmv.json?PRVDR_NUM=01D0026356").read()
obj = json.loads(response.decode('utf-8'))
print (obj[0]['fac_name'])

Here is how to get a response from CMS on a specific CLIA record. Here is the actual table you’re querying. CMS is actually pretty awesome to give access to all of this.

Categories
Uncategorized

_IELinkClickByText in Powershell v2

function ClickLinkByText ([string]$arg1){
$ie.document.Script.execScript("(`$('*:contains(`"" + $arg1 + "`"):last')).click();");
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
}

If you’re using Powershell v2 to automate IE, and the webpage you’re automating has jQuery, try this out.  I’ve been jealous of this function in Autoit, and this is much faster than the “normal” methods to find the link. If you’re on a weak workstation, much more reliable too (the IE DOM randomly fails for me).

Categories
Uncategorized

PowerShell Exception 0x800A01B6

I wanted to draw attention to this link to save somebody that headaches that I had trying to use $ie.Document.getElementById. Hope it helps.

Exception from HRESULT: 0x800A01B6
At line:21 char:1
+ $ie.Document.getElementById("RandomButton").click;while($ie.Busy) { St ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException

https://www.sepago.com/blog/2016/05/03/powershell-exception-0x800a01b6-while-using-getelementsbytagname-getelementsbyname

Always use the following methods instead of the native ones:

  1. IHTMLDocument3_getElementsByTagName
  2. IHTMLDocument3_getElementsByName
  3. IHTMLDocument3_getElementByID

 

Categories
Uncategorized

Finishing Basement / Home Theater Lessons Learned

I wanted to compile a list of all the things I figured out (often too late) while taking on the ridiculous basement project we just recently finished. So, here goes.

  1. HDMI over Cat 6 is a thing. Its utility isn’t immediately obvious, but consider this; You can take **all** of your AV equipment and house it somewhere else just by running the patch cable. This can resolve heat and noise issues without a lot of work. Also, if you have it connected to a HDMI switch, you can attach a bunch of random sources, saving a lot of space and keeping clutter from your living room.
  2. When you decide to put your stereo receiver in a cabinet, make sure the cabinet is the appropriate depth. I didn’t include the volume knob or cable protrusions in the rear in my measurement, and now its a real problem, because I cant shut the cabinet door.
  3. Have a handyman in addition to your contractor so they can work on the little details while other things are going on.
  4. Error on the side of caution when picking your paint sheen. We went too flat and now the tiniest touch leaves a mark that we cant clean off. I didn’t want light reflection from the projector on the walls, but the tradeoff isn’t worth it.
  5. Consider heating the bathroom floor. Katie mentioned it, I laughed it off. Being in the basement, against the concrete floor, the tiles are like ice cubes. Didn’t expect it to be so cold.
  6. Insist that the contractors work within the infrastructure you’ve already had set up. Our electrician (and home theater guy, long story) and general contractor didn’t even think about making sure the new smoke alarms would be connected to the existing hard-wired network of smoke alarms. We had to push and pay more for it.
  7. Without direction, your general contractor will use extra supplies he has stockpiled from past jobs (of variable quality) or the cheapest thing he can use to reasonably accomplish what he set out to do. Random things have been replaced as they’ve been discovered.
  8. Use Amazon Warehouse Deals for supplies. This is a great thread to monitor for things you know you’ll need.  Way cheaper, but a lot of it is unusable.
  9. If you’re not carpeting a huge area, shop remnants. We got a good deal on some nice carpet and used the savings to buy a better pad. Don’t cheap out on the pad.
  10. This is a great time to have z-wave switches and dimmers installed. They work just like any regular switch does, but give you some really cool home automation possibilities later on. I only did one like this, and I regret it. I’ll be replacing all of them at some point.
  11. Examine the contractors work closely every day. They will guess, make mistakes, assume things, and need to be corrected.

I’ll add more as I think of it. We’re happy with the results, but just because you’re not doing it yourself does not mean it will be easy. We still haven’t brought in furniture, finished the cabinets, or put anything decorative up.