Created
November 18, 2020 00:24
-
-
Save ktemkin/bdab27cfff02fddb6de70cb1d6e285bb to your computer and use it in GitHub Desktop.
Working mockup for controlling a JOOFOO lamp with the YS1.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Silly YS1 mockup of an interface for the JOOFOO remote-controlled lamp. | |
""" | |
import sys | |
import argparse | |
# Normally, we'd just import *, but rfcat likes to do things like export functions called str(), | |
# so we'll be a bit more selective. | |
from rflib import RfCat, MOD_ASK_OOK, SYNCM_NONE | |
# Store our frequency targets. | |
CARRIER_FREQUENCY = int(434.64e6) | |
DATA_RATE = int(006.25e3) | |
# Store the bytes that can send each of our two-bit pairs. | |
# Each byte we send to the YS1 encodes two symbols, which we encode as either a 0 (0b1000) | |
# or as a 1 (0b1110) in order to create the proper pattern on transmit. | |
# Note: 0 encodes as [0b1000 = 0x8], 1 encodes as [0b1110 = 0xe]. | |
PATTERNS = { | |
# Full sets of two bytes... | |
"00": 0b0111_0111, | |
"01": 0b0111_0001, | |
"10": 0b0001_0111, | |
"11": 0b0001_0001, | |
# ... and values for any left-over "straggler" bits that can occur at the end of the communication. | |
# We send all zeroes after the relevant bits to generate RF silence. | |
"0": 0b0111_1111, | |
"1": 0b0001_1111 | |
} | |
# | |
# Command Line Interface. | |
# | |
parser = argparse.ArgumentParser(description="YS1 JOOFOO lamp controller.") | |
parser.add_argument('-P', '--power', action="store_const", const="1", default="0") | |
parser.add_argument('-C', '--color', action="store_const", const="1", default="0") | |
parser.add_argument('-U', '--up', action="store_const", const="1", default="0") | |
parser.add_argument('-D', '--down', action="store_const", const="1", default="0") | |
parser.add_argument('-M', '--max', action="store_true") | |
parser.add_argument('-n', '--repeat', type=int, default=8) | |
args = parser.parse_args() | |
# Convenience: if max is set, turn the brightness up all the way by setting UP and a large repeat. | |
if args.max: | |
args.up = "1" | |
args.repeat = 128 | |
# Use the CLI's set of buttons to build a button mask; which we'll eventually send to the device. | |
buttons = f"{args.down}{args.up}{args.color}{args.power}" | |
# If no buttons are pressed in the given mask, fail out early. | |
if buttons == "0000": | |
print("No buttons pressed; nothing to do!\n") | |
sys.exit(0) | |
# | |
# Core script. | |
# | |
# Create a new connection to our RfCat dongle. | |
d = RfCat() | |
# Create a helper function that performs PWM transmissions. | |
def transmit_pwm(bit_string, repeat=1, padding=2): | |
""" Transmits the appropriate PWM values for a string of 1's and 0's. """ | |
bytes_to_send = bytearray() | |
# First, strip all underscores from our bit string, so we can use it as a place separator. | |
bit_string = bit_string.replace('_', '') | |
# As long as we have data left to send, handle sending it. | |
while bit_string: | |
# Extract up to two bits to encode... | |
pair_to_encode = bit_string[0:2] | |
bit_string = bit_string[2:] | |
# ... and encode them into bytes to be sent to the modem. | |
encoded_pair = PATTERNS[pair_to_encode] | |
bytes_to_send.append(encoded_pair) | |
# Pad our bytes to send with data before and after. | |
bytes_to_send = (b"\xFF" * padding) + bytes_to_send + (b"\xFF" * padding) | |
# Set the packet length equal to what we'll be sending. | |
d.makePktFLEN(len(bytes_to_send)) | |
# Finally, transmit the bytes we've generated. | |
for _ in range(repeat): | |
d.RFxmit(bytes_to_send) | |
# Configure the modem to use raw on-off-keying at the lamp's frequency, and at the "data rate" | |
# we've decided to use. This data rate is one quarter the symbol rate; and allows us to create | |
# raw pulse modulated symbols directly from "on"s and "off"s. | |
d.setFreq(CARRIER_FREQUENCY) | |
d.setMdmModulation(MOD_ASK_OOK) | |
d.setMdmDRate(DATA_RATE) | |
d.setMdmSyncMode(SYNCM_NONE) | |
d.setMdmSyncWord(0) | |
# Finally, perform the lamp button presses. | |
transmit_pwm(f"0100_1000_0110_0101_0110_{buttons}_0", repeat=args.repeat) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment