PKYCJ:pyblynkrestapi/BlynkClient.py# -*- coding: utf-8 -*- """ client helpers Leveraging this github repo for the heartbeat https://github.com/erazor83/pyblynk/blob/master/lib/client.py The MIT License (MIT) Copyright (c) 2015 Alexander Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ __author__ = """Alexander Krause """ __date__ = "2016-07-05" __version__ = "0.2.1" __credits__ = """Copyright e-design, Alexander Krause """ __license__ = "MIT" import time import socket from . import common class TCP_Client(object): _Server = None _Port = None _Socket = None _MessageID = None _t_lastRX = None _lastToken = None t_Ping = 5 connected = False def __init__(self, server='blynk-cloud.com', port=8442): self._Server = server self._Port = port def connect(self, timeout=3): #print('connected') self.close() self._MessageID = 0 self._Socket = socket.create_connection( (self._Server, self._Port), timeout ) self._Socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if self._Socket: self.connected = True return self._Socket def close(self): if self._Socket: self._Socket.close() self.connected = False def tx(self, data): # print('tx',data) if self._Socket: try: self._Socket.sendall(data) except Exception: self.connected = False def rx(self, length): if self._Socket: d = [] l = 0 while l < length: r = '' try: r = self._Socket.recv(length - l) self._t_lastRX = time.time() except socket.timeout: # print('rx-timeout') return '' except Exception as e: print('rx exception', str(e)) self.connected = False return '' if not r: self.connected = False return '' d.append(r) # print(d) l = l + len(r) ret = bytes() for cluster in d: ret = ret + cluster return ret def rxFrame(self): response = self.rx(common.ProtocolHeader.size) if response: return common.ProtocolHeader.unpack(response) def txFrame(self, msg_type, data): self.tx( common.ProtocolHeader.pack( msg_type, self.newMessageID(), data ) ) def txFrameData(self, msg_type, data): self.tx( common.ProtocolHeader.pack( msg_type, self.newMessageID(), len(data) ) + data.encode('ascii') ) def newMessageID(self): self._MessageID = self._MessageID + 1 return self._MessageID def auth(self, token=None): if not token and self._lastToken: token = self._lastToken elif token: self._lastToken = token else: return False self.txFrame(common.MSG_LOGIN, len(token)) self.tx(str(token).encode('ascii')) response = self.rxFrame() if response: msg_type, msg_id, msg_status = response if (msg_status == common.MSG_STATUS_OK): print("Auth successfull") return True def Ping(self): #print("Ping...") self.txFrame(common.MSG_PING, 0) rx_frame = self.rxFrame() #print("rx_frame: {}".format(rx_frame)) if rx_frame and \ (rx_frame[0] == common.MSG_RSP) and \ (rx_frame[1] == self._MessageID) and \ (rx_frame[2] == common.MSG_STATUS_OK): #print("...Pong") return True def keepConnection(self): if not self.connected: if self.connect() and self.auth(): return True else: time.sleep(1) return False if (self._t_lastRX + self.t_Ping) < time.time(): self.Ping() PKVEJ pyblynkrestapi/PyBlynkRestApi.pyimport requests from pyblynkrestapi.BlynkClient import TCP_Client import threading import collections import json import logging class PyBlynkException(Exception): pass PinHandler = collections.namedtuple('PinHandler', 'pin_number handler') class PyBlynkRestApi(object): """ http://docs.blynkapi.apiary.io/# Wrapper around the Blynk Rest APIs https://github.com/xandr2/blynkapi/blob/master/blynkapi/Blynk.py https://github.com/xandr2/blynkapi/tree/master/blynkapi """ def __init__(self, auth_token, start_heartbeat=False): self.server = 'blynk-cloud.com' self.port = '80' self.scheme = 'http' self.auth_token = auth_token self.blynk = TCP_Client() self.blynk.connect() self.blynk.auth(auth_token) self.read_pin_handlers = [] self.set_pin_handlers = [] self.handlers = [] self.run_interval = None self.is_stopped = False if start_heartbeat: self.start_blynk_heartbeat() self.logger = logging.getLogger() self.logger.setLevel(logging.DEBUG) def _get_blynk_server_url(self): return "{0}://{1}:{2}/{3}".format(self.scheme, self.server, self.port, self.auth_token) def start_blynk_heartbeat(self): if not self.is_stopped: self.blynk.keepConnection() threading.Timer(2, self.start_blynk_heartbeat).start() def get_pin_value(self, pin_name): """ Read the value from the Blynk App for the pin specified. For example, a common use case is to create a Button in the Blynk app, give it a pin number such as V1, and have this method read the value of the Blynk App button. :param pin_name: :return: Array of String values. Button: single element array of zero or one. If the button has not been pressed then the value is an empty string Slider: single element array of the slider value. JoyStick: two element array of the X/Y values. In the definition of the element, [0] element is assigned a virtual pin, and the [1] element is assigned a different virtual pin. The pin_name can be either of the pins assigned to the joystick ZeRGBa: three element array with the order in the array being: Red,Green,Blue values The pin_name can be any of the pins assigned to the zeRGBa element. """ if not pin_name: raise PyBlynkException(message="Invalid pin_name: {}".format(pin_name)) try: url = "{}/get/{}".format(self._get_blynk_server_url(), pin_name) response_body = requests.get(url,{}) #print(response_body.json()) pin_value = response_body.json() if len(pin_value) == 1: pin_value = pin_value[0] except: self.logger.exception("Read Pin Error") pin_value = None return pin_value def set_pin_value(self, pin_name, value): if not pin_name: raise PyBlynkException(message="Invalid pin_name: {}".format(pin_name)) try: url = "{}/update/{}".format(self._get_blynk_server_url(), pin_name) payload = """ [ "{}" ] """.format(value) headers = { 'Content-Type': 'application/json' } requests.put(url, data=payload, headers=headers, verify=False) except: self.logger.exception("Set Pin Error") #print("set_pin_value: {},{}".format(url, payload)) def led_on(self, pin_name): self.set_pin_value(pin_name, 255) def led_off(self, pin_name): self.set_pin_value(pin_name, 0) def set_led(self, pin_name, value): if value == 0: self.led_off(pin_name) else: self.led_on(pin_name) def set_lcd(self, line0_pin_number, line0_value, line1_pin_number, line1_value): self.set_pin_value(line0_pin_number, line0_value) self.set_pin_value(line1_pin_number, line1_value) def add_read_pin_handler(self, pin_number, handler): ph = PinHandler(pin_number=pin_number, handler=handler) self.read_pin_handlers.append(ph) def add_set_pin_handler(self, pin_number, handler): ph = PinHandler(pin_number=pin_number, handler=handler) self.set_pin_handlers.append(ph) def add_handler(self, handler): ph = PinHandler(pin_number=-1, handler=handler) self.handlers.append(ph) def _run_read_handlers(self): # loop through all of the read pins for pin_handler in self.read_pin_handlers: try: pin_value = self.get_pin_value(pin_handler.pin_number) pin_handler.handler(pin_value, pin_handler.pin_number, self) except: self.logger.exception("Exception processing read pin handlers") # reset the timer to call back again threading.Timer(self.run_interval, self._run_read_handlers).start() def _run_set_handlers(self): # loop through all fo the set pins for pin_handler in self.set_pin_handlers: try: pin_value = pin_handler.handler(pin_handler.pin_number, self) self.set_pin_value(pin_handler.pin_number, pin_value) except: self.logger.exception("Exception processing read pin handlers") # reset the timer to call back again threading.Timer(self.run_interval, self._run_set_handlers).start() def _run_handlers(self): # loop through all of the generic handlers for pin_handler in self.handlers: try: pin_handler.handler(self) except: self.logger.exception("Exception processing read pin handlers") # reset the timer to call back again threading.Timer(self.run_interval, self._run_handlers).start() def run(self, interval): self.run_interval = interval self._run_read_handlers() self._run_set_handlers() self._run_handlers() def send_push_notification(self, contents): """ :param contents: :return: """ payload = { 'body': "'{}'".format(contents), } headers = { 'Content-Type': 'application/json' } url = "{}/notify".format(self._get_blynk_server_url()) response = requests.post(url, data=json.dumps(payload), headers=headers, verify=False) if response.status_code != 200: raise Exception("Send Notify Error: {}".format(response.content)) def send_email(self, subject, contents, to_email_address=None): """ Send email to the specified address or if no to_email_address is specified then use the email address in the email widget For this api to work, there has to be an email widget in the app and the app needs to be running. :param subject: Email subject :param title: Email contents :param to_email_address: optional, address to send to. If None then use the widgets email address. :return: void, Exception if status is not 200 """ if to_email_address: payload = { 'to': to_email_address, 'title': contents, 'subj': subject } else: payload = { 'title': contents, 'subj': subject } headers = { 'Content-Type': 'application/json' } url = "{}/email".format(self._get_blynk_server_url()) response = requests.post(url, data=json.dumps(payload), headers=headers, verify=False) if response.status_code != 200: raise Exception("Send Email Error: {}".format(response.content)) PK,EJ1pyblynkrestapi/__init__.py""" PyBlynkRestApi Python wrapper around the Blynk Rest API. See documenation at: https://github.com/youngsoul/PyBlynkRestApi """ __version__ = '0.0.1' PKmAJ Tpyblynkrestapi/common.py# -*- coding: utf-8 -*- """ commmon helpers https://github.com/erazor83/pyblynk/blob/master/lib/common.py The MIT License (MIT) Copyright (c) 2015 Alexander Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ __author__ = """Alexander Krause """ __date__ = "2016-01-11" __version__ = "0.2.0" __credits__ = """Copyright e-design, Alexander Krause """ __license__ = "MIT" import struct MSG_RSP = 0 MSG_LOGIN = 2 MSG_PING = 6 MSG_BRIDGE = 15 MSG_HW = 20 MSG_STATUS_OK = 200 ProtocolHeader = struct.Struct("!BHH") def ArgsToBuffer(*args): # Convert params to string and join using \0 return "\0".join(map(str, args)) def BufferToArgs(buff): return (buff.decode('ascii')).split("\0") PK!H|&Ub$pyblynkrestapi-0.0.1.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q034 /, (-JLR()*M ILR(4KM̫#DPK!H;={1'pyblynkrestapi-0.0.1.dist-info/METADATA] w(T2Y'5Fo%[g8|r/@h2&흠;'WRP&SeBlGZE[9nPb'oe` 1$Q[φz.Ù$}Fcz^P}FZЦ^cǮ;Po# PK!Hٲe4%pyblynkrestapi-0.0.1.dist-info/RECORDn@} :: :̈́0-eOߞ4EKQdӑ=mʞ/F!Mu :YpTnCBb:XEN4efT%\@sïGvGTݦ eE7YIbƌYҀ=”qJN"Dm8 Hԅ&4(C b T9|h#(XE&.4<=7>6aɋ}F#V>qzמTG4o٬I#}G>q|]&bj C$|>w PKYCJ:pyblynkrestapi/BlynkClient.pyPKVEJ pyblynkrestapi/PyBlynkRestApi.pyPK,EJ14pyblynkrestapi/__init__.pyPKmAJ T5pyblynkrestapi/common.pyPK!H|&Ub$<pyblynkrestapi-0.0.1.dist-info/WHEELPK!H;={1'=pyblynkrestapi-0.0.1.dist-info/METADATAPK!Hٲe4%g>pyblynkrestapi-0.0.1.dist-info/RECORDPK!@