class myFCM: ID = myConfig.get('fcm', 'userId') API = myConfig.get('fcm', 'url') def __init__(self): self.SESSION = httplib2.Http() def send(self, rcpt, msg): dat = msg.copy(); dat.update({ 'ttl': 1000, 'priority': 'high', 'id': self.ID }); to = rcpt.split('=') try: dat[to[0]] = to[1] except IndexError: dat['device'] = rcpt response, content = self.SESSION.request( self.API, method="POST", headers={'Content-Type': 'application/json; charset=UTF-8'}, body=json.dumps(dat) ) return response.status == 200 and content.decode() == '{}'
class myMQTT: MQTTuser = myConfig.get('mqtt', 'user') MQTTpass = myConfig.get('mqtt', 'password') MQTTbroker = myConfig.get('mqtt', 'host') MQTTport = myConfig.get('mqtt', 'port') client = None def __init__(self): self.getSensors() def reload(self): self.getSensors() def getSensors(self): self.sensorsByAddr = {} self.sensorsByID = {} sensorsDB = mySensors() sensors = sensorsDB.getSensors({"type":"mqtt"}).values() for sensor in sensors: if not sensor['sensor'] in self.sensorsByAddr: self.sensorsByAddr[sensor['sensor']] = {} self.sensorsByAddr[sensor['sensor']] = sensor.copy() if not sensor['sensor_id'] in self.sensorsByID: self.sensorsByID[sensor['sensor_id']] = {} self.sensorsByID[str(sensor['sensor_id'])] = sensor.copy() sensorsDB.closeDB() def getSensorByAddr(self, addr): try: return self.sensorsByAddr[addr] except: return None def getSensorByID(self, id): try: return self.sensorsByID[str(id)] except: return None def updateSensorByAddr(self, addr, state, commit = True): ok = False sensor = self.getSensorByAddr(addr) #print(addr + " " + state) self.log(str(addr) + " " + state) if sensor and 'sensor_id' in sensor: try: # unable to use self.mySensors - mqtt callback is in another thred #print(str(sensor['sensor_id']) + " " + state) sensors = mySensors() sensors.saveSensorState(sensor['sensor_id'], state, commit) sensors.closeDB() self.log("Switched sensor " + str(sensor['sensor_id']) + " " + state) except Exception, e: self.log("*** ERROR *** " + str(e)) pass ok = True #else: #raise Exception("Sensor '" + addr + "' not found.") return ok
class myFCM: ID = myConfig.get('fcm', 'userId') API = myConfig.get('fcm', 'url') errorCounter = 0 def __init__(self): self.SESSION = httplib2.Http() def send(self, rcpt, msg): dat = msg.copy() dat.update({ 'ttl': 1000, 'priority': 'high', 'id': self.ID }) to = rcpt.split('=') try: dat[to[0]] = to[1] except IndexError: dat['device'] = rcpt success = False while not success and self.errorCounter < 2: try: response, content = self.SESSION.request( self.API, method="POST", headers={ 'Content-Type': 'application/json; charset=UTF-8' }, body=json.dumps(dat)) if response.status == 200 and content.decode() == '{}': self.errorCounter = 0 success = True else: self.errorCounter += 1 time.sleep(1) except: self.errorCounter += 1 time.sleep(1) if not success: fcmSensor = 23 state = mySensors().getSensor(fcmSensor) if state['state'] == 'ON': mySensors().saveSensorState(fcmSensor, 'OFF', True) return success
def _start(): comet = CometClient.CometClient(onMessage) comet.listen(myConfig.get('fcm', 'websocket'))
class myTelegram: TOKEN = myConfig.get('telegram', 'token') URL = "https://api.telegram.org/bot{}/" def __init__(self): self.URL = self.URL.format(self.TOKEN) self.timeout = 5 self.last_update_id = None def setTimeout(self, timeout): self.timeout = timeout def http(self): if not hasattr(self, '_http'): self._http = httplib2.Http(timeout=self.timeout) return self._http def enableLog(self): self.logHandler = myLogs('telegram-class', 0o666) def log(self, data): if hasattr(self, 'logHandler'): self.logHandler.log(data) def get_url(self, url): attempts = 20; while attempts > 0: try: response, data = self.http().request(url) if response.status == 200: content = data.decode("utf8") return content except Exception as e: attempts -= 1 if (attempts < 1): #fcm = myfcm(); #data = {"message": {"message":"telegram unable to connect",'cmd': 'showalert','from': 'raspberry'}} #time.sleep(60) raise time.sleep(1) def post_json(self, url, json): attempts = 20; while attempts > 0: try: response, data = self.http().request( uri=url, method='POST', headers={'Content-Type': 'application/json; charset=UTF-8'}, body=json, ) if response.status == 200: content = data.decode("utf8") return content except Exception as e: attempts -= 1 if (attempts < 1): #fcm = myfcm(); #data = {"message": {"message":"telegram unable to connect",'cmd': 'showalert','from': 'raspberry'}} #time.sleep(60) raise time.sleep(1) def get_json_from_url(self, url): content = self.get_url(url) js = json.loads(content) return js def get_updates(self, offset=None): tmout = self.timeout - 5 if tmout < 1: tmout = 2 url = self.URL + "getUpdates?timeout=" + str(tmout) if offset: url += "&offset={}".format(offset) js = self.get_json_from_url(url) return js def get_last_update_id(self, updates): update_ids = [] for update in updates["result"]: update_ids.append(int(update["update_id"])) return max(update_ids) def echo_all(self, updates): for update in updates["result"]: #{'update_id': 968412429, 'channel_post': {'chat': {'type': 'channel', 'title': 'HomePi', 'id': -1001XXXXXXXX}, 'text': 'Hi', 'message_id': 4, 'date': 1541345540}} try: if 'message' in update: #direct bot message text = update["message"]["text"] chat = update["message"]["chat"]["id"] elif 'channel_post' in update: text = update['channel_post']['text'] chat = update["channel_post"]["chat"]["id"] self.log("Got new message in " + str(chat) + ": " + text) text = re.compile('\W').split(text)[0] subprocess.Popen(['/home/scripts/actions/on-telegram-message.py', text], stdout=subprocess.PIPE).stdout.read().strip() #self.send_message('I have got: ' + text, chat) except Exception as e: self.log("Error processing message:" + str(e)) #self.send_message('Error : ' + str(e), -1001420XXXXX) pass def get_last_chat_id_and_text(self, updates): num_updates = len(updates["result"]) last_update = num_updates - 1 text = updates["result"][last_update]["message"]["text"] chat_id = updates["result"][last_update]["message"]["chat"]["id"] return (text, chat_id) def send_message(self, text, chat_id, silent = False): #text = urllib.parse.quote_plus(text) #url = self.URL + "sendMessage?text={}&chat_id={}{}".format(text, chat_id, ('' if silent == False else '&disable_notification=true')) url = self.URL + "sendMessage" data = { 'text': text, 'chat_id': chat_id, 'disable_notification': silent == False, #'reply_markup': {'inline_keyboard': [{ #'text':'First Btn', #'url':'http://dobovo.com', #}]} } #print("sending" + json.dumps(data)) return self.post_json(url, json.dumps(data)); #return self.get_url(url) def send_photo(self, file, chat_id, silent = False): try: #url = "https://api.telegram.org/bot<Token>/sendPhoto"; url = self.URL + "sendPhoto" if silent == True: url += '&disable_notification=true' files = [('photo', 'image.jpg', open(file, 'rb'))] fields = [('chat_id', str(chat_id))] content_type, body = MultipartFormdataEncoder().encode(fields, files) response, data = self.http().request( url, method="POST", headers={'Content-Type': content_type + '; charset=UTF-8'}, body=body ) return response.status == 200 except: return False def start(self): #Long Pooling enabled by self.timeout while True: self.checkServer() time.sleep(0.1) def checkServer(self): updates = self.get_updates(self.last_update_id) #print(updates) self.processMessage(updates) def processMessage(self, updates): if len(updates["result"]) > 0: self.last_update_id = self.get_last_update_id(updates) + 1 self.echo_all(updates) # old version with requests module #def startBot(self): #import CometClient #self.enableLog() #self.log("Starting bot") #comet = CometClient.CometClient(self.messageHandler) #timeout = 60 #while True: #url = self.URL + "getUpdates?timeout=" + str(timeout-10) #if self.last_update_id: #url += "&offset={}".format(self.last_update_id) ##print("reconnect" + url) #comet.get(url, timeout) def messageHandler(self, data): #print(data) js = json.loads(data.decode("utf-8")) self.processMessage(js) def stop(self): pass
class myTelegram: TOKEN = myConfig.get('telegram', 'token') URL = "https://api.telegram.org/bot{}/" #URL = "https://izbushka.kiev.ua/tmp/test.json?{}" def __init__(self): self.URL = self.URL.format(self.TOKEN) self.timeout = 5 self.last_update_id = None def setTimeout(self, timeout): self.timeout = timeout def http(self): if not hasattr(self, '_http'): self._http = httplib2.Http(timeout=self.timeout) return self._http def enableLog(self): self.logHandler = myLogs('telegram-class', 0o666) def log(self, data): if hasattr(self, 'logHandler'): self.logHandler.log(data) def get_url(self, url): attempts = 20 while attempts > 0: try: response, data = self.http().request(url) if response.status == 200: content = data.decode("utf8") return content else: self.log("Got non-200 response: " + str(response.status) + ": " + data.decode("utf8")) except Exception as e: self.log("Cann't fetch url: " + str(e)) attempts -= 1 if (attempts < 1): #fcm = myfcm(); #data = {"message": {"message":"telegram unable to connect",'cmd': 'showalert','from': 'raspberry'}} #time.sleep(60) raise time.sleep(1) def post_json(self, url, json, attempts=20): while attempts > 0: try: response, data = self.http().request( uri=url, method='POST', headers={ 'Content-Type': 'application/json; charset=UTF-8' }, body=json, ) if response.status == 200: content = data.decode("utf8") return content elif response.status == 400 and attempts > 1: attempts = 1 # try one more time, just in case else: self.log("Wrong telegram response. Code: " + str(response) + " " + str(data)) except Exception as e: self.log("API call failed " + str(e)) attempts = attempts - 1 time.sleep(1) #fcm = myfcm(); #data = {"message": {"message":"telegram unable to connect",'cmd': 'showalert','from': 'raspberry'}} raise Exception(response, data) def get_json_from_url(self, url): content = self.get_url(url) js = json.loads(content) return js def get_updates(self, offset=None): tmout = self.timeout - 5 if tmout < 1: tmout = 2 url = self.URL + "getUpdates?timeout=" + str(tmout) if offset: url += "&offset={}".format(offset) js = self.get_json_from_url(url) return js def get_last_update_id(self, updates): update_ids = [] for update in updates["result"]: update_ids.append(int(update["update_id"])) return max(update_ids) def echo_all(self, updates): for update in updates["result"]: #{'update_id': 968412429, 'channel_post': {'chat': {'type': 'channel', 'title': 'HomePi', 'id': -1001XXXXXXXX}, 'text': 'Hi', 'message_id': 4, 'date': 1541345540}} try: # self.log("new event" + str(update)) if 'message' in update: #direct bot message text = update["message"]["text"] chat = update["message"]["chat"]["id"] self.log("Got new message in " + str(chat) + ": " + text) elif 'channel_post' in update: text = update['channel_post']['text'] chat = update["channel_post"]["chat"]["id"] self.log("Got new post in " + str(chat) + ": " + text) elif 'callback_query' in update: text = update["callback_query"]['data'] chat = update["callback_query"]["message"]["chat"]["id"] self.log("Got new callback in " + str(chat) + ": " + text) else: self.log("Got UNKNOWN event " + str(update)) continue subprocess.Popen( ['/home/scripts/actions/on-telegram-message.py', text], stdout=subprocess.PIPE).stdout.read().strip() #self.send_message('I have got: ' + text, chat) except Exception as e: self.log("Error processing message:" + str(e)) #self.send_message('Error : ' + str(e), -1001420XXXXX) pass def get_last_chat_id_and_text(self, updates): num_updates = len(updates["result"]) last_update = num_updates - 1 text = updates["result"][last_update]["message"]["text"] chat_id = updates["result"][last_update]["message"]["chat"]["id"] return (text, chat_id) def send_message(self, text, chat_id, silent=False, showKeyboard=False): #text = urllib.parse.quote_plus(text) #url = self.URL + "sendMessage?text={}&chat_id={}{}".format(text, chat_id, ('' if silent == False else '&disable_notification=true')) url = self.URL + "sendMessage" if (int(chat_id) < -10000000000): #private channel keyboard = { "inline_keyboard": [[ { "text": "Help", "callback_data": "help" }, { "text": "Hall", "callback_data": "hall" }, { "text": "Bighall", "callback_data": "bighall" }, { "text": "Room", "callback_data": "room" }, { "text": "Kitchen", "callback_data": "kitchen" }, ]] } else: keyboard = { "keyboard": [[ { "text": "Help", "callback_data": "help" }, { "text": "Hall", "callback_data": "hall" }, { "text": "Bighall", "callback_data": "bighall" }, { "text": "Room", "callback_data": "room" }, { "text": "Kitchen", "callback_data": "kitchen" }, ]], "resize_keyboard": True } data = { 'text': text, 'chat_id': chat_id, 'disable_notification': silent == False, 'reply_markup': keyboard if showKeyboard else {} } #print("sending" + json.dumps(data)) return self.post_json(url, json.dumps(data)) #return self.get_url(url) def send_photo(self, file, chat_id, silent=False): try: #url = "https://api.telegram.org/bot<Token>/sendPhoto"; url = self.URL + "sendPhoto" if silent == True: url += '&disable_notification=true' # open(file, 'rb') will be closed automatically in MultipartFormdataEncoder files = [('photo', 'image.jpg', open(file, 'rb'))] fields = [('chat_id', str(chat_id))] content_type, body = MultipartFormdataEncoder().encode( fields, files) response, data = self.http().request( url, method="POST", headers={'Content-Type': content_type + '; charset=UTF-8'}, body=body) return response.status == 200 except: return False def start(self): #Long Pooling enabled by self.timeout self.enableLog() while True: self.checkServer() time.sleep(0.1) def checkServer(self): updates = self.get_updates(self.last_update_id) #print(updates) self.processMessage(updates) def processMessage(self, updates): if len(updates["result"]) > 0: self.last_update_id = self.get_last_update_id(updates) + 1 self.echo_all(updates) # old version with requests module #def startBot(self): #import CometClient #self.enableLog() #self.log("Starting bot") #comet = CometClient.CometClient(self.messageHandler) #timeout = 60 #while True: #url = self.URL + "getUpdates?timeout=" + str(timeout-10) #if self.last_update_id: #url += "&offset={}".format(self.last_update_id) ##print("reconnect" + url) #comet.get(url, timeout) def messageHandler(self, data): #print(data) js = json.loads(data.decode("utf-8")) self.processMessage(js) def stop(self): pass
class myMQTT: MQTTuser = myConfig.get('mqtt', 'user') MQTTpass = myConfig.get('mqtt', 'password') MQTTbroker = myConfig.get('mqtt', 'host') MQTTport = int(myConfig.get('mqtt', 'port')) client = None def __init__(self): self.getSensors() def reload(self): self.getSensors() def getSensors(self): self.sensorsByAddr = {} self.sensorsByID = {} sensorsDB = mySensors() sensors = sensorsDB.getSensors({"type":"mqtt"}).values() for sensor in sensors: if not sensor['sensor'] in self.sensorsByAddr: self.sensorsByAddr[sensor['sensor']] = {} self.sensorsByAddr[sensor['sensor']] = sensor.copy() if not sensor['sensor_id'] in self.sensorsByID: self.sensorsByID[sensor['sensor_id']] = {} self.sensorsByID[str(sensor['sensor_id'])] = sensor.copy() sensorsDB.closeDB() del sensorsDB def getSensorByAddr(self, addr): try: return self.sensorsByAddr[addr] except: return None def getSensorByID(self, id): try: return self.sensorsByID[str(id)] except: return None def updateSensorByAddr(self, addr, state, commit = True): ok = False sensor = self.getSensorByAddr(addr) #print(addr + " " + state) self.log(str(addr) + " " + state) if sensor and 'sensor_id' in sensor: try: #print(str(sensor['sensor_id']) + " " + state) # WARNING! # 1. unable to use self.mySensors - mqtt callback is in another thred # 2. unable to update DB here - memory leak, every updateSensorByAddr leak 4kb # 3. tmp solution - actions/save_sensor_state.py # TODO: sqlite multithread (queue to standalone process) #sensors = mySensors() #sensors.saveSensorState(sensor['sensor_id'], state, commit) #sensors.closeDB() subprocess.Popen(['/home/scripts/actions/save_sensor_state.py', str(sensor['sensor_id']), state]); self.log("Switched sensor " + str(sensor['sensor_id']) + " " + state) except Exception as e: self.log("*** ERROR *** " + str(e)) pass ok = True #else: #raise Exception("Sensor '" + addr + "' not found.") del sensor return ok def commit(self): pass #self.mySensors.saveDB() def switchByAddr(self, addr, state): topic = "control/" + addr; #print (topic + " " + state) self.log("MQTT publish to " + topic) if state[0:1] == '{': # json self.client.publish(topic, state) else: self.client.publish(topic,"{\"state\":\"" + state + "\"}") # TODO: MQTT CALL #urlreq.urlopen('http://' + addr + '/' + state.lower()) def switchById(self, id, state): sensor = self.getSensorByID(id); if sensor and 'sensor' in sensor: addr = sensor['sensor'] self.switchByAddr(addr, state) # TODO: MQTT CALL #response = urlreq.urlopen('http://' + addr + '/' + state.lower()) return None def onDisconnect(self, client, userdata, rc): self.log("--- GOT MQTT DISCONNECT") def onConnect(self, client, userdata, flags, rc): if (self.doSubscribe): self.subscribe() self.log("+++ MQTT CONNECTED!") def onMqttMessage(self, client, userdata, message): #print(message.topic + ": message ",str(message.payload.decode("utf-8"))) topic = message.topic.split('/') data = json.loads(str(message.payload.decode("utf-8"))) self.log(" GOT MQTT message on " + message.topic + " " + str(message.payload)) if topic[0] == "sensors": self.updateSensorByAddr("/".join(topic[1:]), data["state"]); elif topic[0] == "lwt": host = topic[1] for addr in self.sensorsByAddr: path = addr.split('/') if path[0] == host: sensor = self.sensorsByAddr[addr] if sensor['group'] == 'light-switch': self.updateSensorByAddr(addr, 'ERR'); del topic del data def log(self, msg): logs = myLogs('mqtt-messages') logs.log(msg) pass def connect(self, subscribe = False): self.doSubscribe = subscribe self.client = paho.Client("PythonMQTT-" + str(time.time())) self.client.username_pw_set(username=self.MQTTuser, password=self.MQTTpass) res = self.client.connect(self.MQTTbroker, self.MQTTport) self.client.on_message = self.onMqttMessage self.client.on_disconnect = self.onDisconnect self.client.on_connect = self.onConnect self.log(" *** Connecting to MQTT") def subscribe(self): #self.client.loop_start() self.client.subscribe("sensors/#") self.client.subscribe("lwt/#") def disconnect(self): self.log(" *** Disconnecting") self.client.disconnect() #disconnect self.client.loop_stop() #stop loop def loop(self): self.client.loop_forever()