class DISTANCE_SENSOR(parlay.ParlayCommandItem): TRIG_PIN = parlay.ParlayProperty(val_type=int, default=13) ECHO_PIN = parlay.ParlayProperty(val_type=int, default=15) DISTANCE = parlay.ParlayDatastream(default=0) POLLING = parlay.ParlayProperty(val_type=int, default=0) POLLING_INTERVAL = parlay.ParlayProperty(val_type=int, default=1) @parlay.parlay_command() def init(self): GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location GPIO.setup(self.TRIG_PIN, GPIO.OUT) GPIO.setup(self.ECHO_PIN, GPIO.IN) @parlay.parlay_command() def poll(self): GPIO.output(self.TRIG_PIN, 0) time.sleep(0.000002) # let it settle GPIO.output(self.TRIG_PIN, 1) time.sleep(0.00001) # let it settle GPIO.output(self.TRIG_PIN, 0) #busy loop. wait for echo pin to go high while GPIO.input(self.ECHO_PIN) == 0: pass time1 = time.time() while GPIO.input(self.ECHO_PIN) == 1: pass time2 = time.time() duration = time2 - time1 self.DISTANCE = duration * 340 / 2 * 100 return self.DISTANCE @parlay.parlay_command() def poll_forever(self): while True: self.poll() time.sleep(self.POLLING_INTERVAL)
class RG_LED(parlay.ParlayCommandItem): R_PIN = parlay.ParlayProperty(val_type=int, default=11) G_PIN = parlay.ParlayProperty(val_type=int, default=12) @parlay.parlay_command() def init(self): GPIO.setup(self.R_PIN, GPIO.OUT) GPIO.output(self.R_PIN, GPIO.HIGH) GPIO.setup(self.G_PIN, GPIO.OUT) GPIO.output(self.G_PIN, GPIO.HIGH) self.p_R = GPIO.PWM(self.R_PIN, 2000) # set Frequency to 2KHz self.p_G = GPIO.PWM(self.G_PIN, 2000) self.p_R.start(0) # Initial duty Cycle = 0(leds off) self.p_G.start(0) @parlay.parlay_command() def turn_green(self): self.p_R.ChangeDutyCycle(0) self.p_G.ChangeDutyCycle(100) @parlay.parlay_command() def turn_red(self): self.p_G.ChangeDutyCycle(0) self.p_R.ChangeDutyCycle(100) @parlay.parlay_command() def turn_off(self): self.p_G.ChangeDutyCycle(0) self.p_R.ChangeDutyCycle(0) @parlay.parlay_command() def blink(self, times): for x in range(int(times)): self.turn_green() sleep(1) self.turn_red() sleep(1)
class Adder(parlay.ParlayCommandItem): # all of these parameters are optional, but recommended to x = parlay.ParlayProperty(default=0, val_type=int) y = parlay.ParlayProperty(default=0, val_type=int) result = parlay.ParlayDatastream(default=0) @parlay.parlay_command() def echo(self, text): """ Will Echo whatever was sent to the python console """ print(text) @parlay.parlay_command() def add(self): """ Will add x and y and then return and echo the result """ self.result = self.x + self.y self.echo(self.result) return self.result
class CloudStressTest(parlay.ParlayCommandItem): data = parlay.ParlayDatastream(val_type=float, default=0.0) sleep_time = parlay.ParlayProperty(val_type=float, default=1) @parlay.parlay_command() def count_up(self, max): """ :param max: :type max int :return: """ for x in xrange(max): self.data = x time.sleep(self.sleep_time)
class LCD(parlay.ParlayCommandItem): TEXT = parlay.ParlayProperty(val_type=str, default="Hello World") @parlay.parlay_command() def init(self): LCD1602.init(0x27, 1) # init(slave address, background light) LCD1602.write(0, 0, 'Hello') LCD1602.write(1, 1, 'World') time.sleep(1) LCD1602.clear() @parlay.parlay_command() def loop(self): current_text = "" while True: "Scroll Text" # add whitespace if len(self.TEXT) > 16: text = self.TEXT # fancy scrolling if we have a long string for i in range(len(text)): #break if text changes if text != self.TEXT: break LCD1602.write(0, 0, text) text = text[1:] time.sleep(0.5) LCD1602.clear() elif current_text != self.TEXT: #static change LCD1602.clear() LCD1602.write(0, 0, self.TEXT) current_text = self.TEXT time.sleep(1) # hold for at least 1 second else: # sleep poll time.sleep(1)
class CloudLink(parlay.ParlayCommandItem): connected_to_cloud = parlay.ParlayProperty(val_type=bool, default=False, read_only=True) base_link_uri = parlay.ParlayProperty( val_type=str, default=CloudLinkSettings.CLOUD_SERVER_ADDRESS + "/channels") device_registration_uri = parlay.ParlayProperty( val_type=str, default=CloudLinkSettings.CLOUD_SERVER_ADDRESS + "/device_stats/api/v1/device") uuid = parlay.ParlayProperty(val_type=str) def __init__(self): parlay.ParlayCommandItem.__init__(self, "parlay.items.cloud_link", "Cloud Link") self._http_agent = Agent(self._reactor) self.channel_uri = None self.cloud_factory = None if CloudLinkSettings.PRIVATE_KEY_LOCATION is None: raise RuntimeError( "CloudLinkSettings.PRIVATE_KEY_LOCATION must be set for cloud to work" ) if CloudLinkSettings.UUID_LOCATION is None: raise RuntimeError( "CloudLinkSettings.UUID_LOCATION must be set for cloud to work" ) try: with open(CloudLinkSettings.UUID_LOCATION, 'r') as uuid_file: self.uuid = uuid_file.read() except IOError: logger.warn( "Error reading UUID file. Has this device been registered?") self.uuid = "" @parlay.parlay_command(async=True) def get_public_key(self): if CloudLinkSettings.PUBLIC_KEY_LOCATION is None: raise Exception( "Public Key Location not Set. Please set CloudLinkSettings.PUBLIC_KEY_LOCATION" ) # simply open and read it with open(CloudLinkSettings.PUBLIC_KEY_LOCATION) as public_key_file: return public_key_file.read() @parlay.parlay_command(async=True) def generate_keys(self): private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) private_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.BestAvailableEncryption( bytes(CloudLinkSettings.PRIVATE_KEY_PASSPHRASE))) public_pem = private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) with open(CloudLinkSettings.PRIVATE_KEY_LOCATION, 'w') as key_file: key_file.write(str(private_pem)) with open(CloudLinkSettings.PUBLIC_KEY_LOCATION, 'w') as key_file: key_file.write(str(public_pem)) @parlay.parlay_command() def register_device_with_cloud(self, username, password, name, serial, group_id, notes): """ Register the public and private keys with the cloud. If you already have them registered this will overwrite them :param username: username of a user with access to the cloud :param password: password of the user to authenticate with :param name: the name of the device :param serial: the serial number of the device :param group_id: the id given to the access group for this device :type group_id int :param notes: any notes to attach to this device :return: the UUID of the newly created device """ result = requests.post(self.device_registration_uri, data={ 'name': name, 'serial': serial, 'group': group_id, 'notes': notes, "public_key": self.get_public_key() }, auth=(username, password)) if result.ok: self.uuid = result.json()["UUID"] with open(CloudLinkSettings.UUID_LOCATION, 'w+') as uuid_file: uuid_file.write(self.uuid) return self.uuid else: raise RuntimeError("Error registering device: " + str(result.status_code) + " - " + result.text) @parlay.parlay_command(async=True) def http_request(self, path="/", encode='zlib'): """ Do an http request on the broker :type path str """ url = "http://localhost:" + str(Broker.get_instance().http_port) + path print url # http"://localhost:broker.http_port request = self._http_agent.request( 'GET', url, Headers({'User-Agent': ['Twisted Web Client']}), None) request.addCallback(lambda response: readBody(response)) request.addCallback(lambda html: (base64.b64encode(html.encode(encode)) ) if encode == "zlib" else html) return request @parlay.parlay_command(async=True) def connect_to_cloud_channel(self): """ Connect up to the cloud channel with UUID uuid :param uuid: the UUID of this device. The uuid determines which channel you are connected to :return: """ if self.uuid is None or self.uuid == "": raise RuntimeError( "Must have a valid UUID to connect to the cloud") if self.connected_to_cloud: raise RuntimeError("Already Connected to cloud") yield self.get_channel() # cool, now set up the websocket connection to that channel reactor = self._adapter.reactor print "attempting to connect to", self.channel_uri self.cloud_factory = CloudLinkWebsocketClientFactory(self, self.channel_uri, reactor=reactor) self.cloud_factory.protocol = CloudLinkWebsocketClient self.cloud_factory.continueTrying = True # attempt to reconnect if self.channel_uri.startswith("wss"): reactor.connectSSL(self.cloud_factory.host, self.cloud_factory.port, self.cloud_factory, ssl.ClientContextFactory()) else: reactor.connectTCP(self.cloud_factory.host, self.cloud_factory.port, self.cloud_factory) @run_in_thread def get_channel(self): #first get a token we need to sign in order to prove we are who we say we are r = requests.get(str(self.base_link_uri) + "/get_device_token", params={ "UUID": self.uuid, }) token = r.json()["token"] # get the private Key with open(CloudLinkSettings.PRIVATE_KEY_LOCATION, 'r') as key_file: private_key = serialization.load_pem_private_key( key_file.read(), password=CloudLinkSettings.PRIVATE_KEY_PASSPHRASE, backend=default_backend()) # sign the token with our private key signer = private_key.signer(padding.PKCS1v15(), hashes.SHA256()) signer.update(bytes(token)) signature = signer.finalize() # get the randomly assigned channel for my UUID r = requests.get(str(self.base_link_uri) + "/get_device_group", params={ "UUID": self.uuid, "signature": b64encode(signature), "format": "PKCS1_v1_5" }) if r.ok: self.channel_uri = r.json()["channel"] elif r.status_code == 400: raise Exception("UUID or Token not registered with Cloud.") elif r.status_code == 403: raise Exception( "Signature didn't verify correctly. Bad private key or signature." ) @parlay.parlay_command(async=True) def disconnect_from_cloud(self): self.cloud_factory.continueTrying = False # stop retrying self.cloud_factory.disconnect()