class XiaolanSkillFlightSearcher: def __init__(self, log, setting, text): self.log = log self.setting = setting self.text = text self.tts = BaiduTts(log, setting) self.stt = BaiduStt(log, setting) self.recorder = XiaolanRecorder(log, setting) self.player = XiaolanPlayer(log, setting) def request_csairlines(self, dep, arr, date, cities_info_list, token): flight_list = {} url = "https://b2c.csair.com/portal/flight/direct/query" dep_code = cities_info_list[dep.decode()][0] arr_code = cities_info_list[arr.decode()][0] self.log.add_log("Skill-FlightSearcher: dep city code: " + dep_code, 1) self.log.add_log("Skill-FlightSearcher: arr city code: " + arr_code, 1) param = { "depCity": dep_code, "arrCity": arr_code, "flightDate": str(date), "adultNum": "1", "childNum": "0", "infantNum": "0", "cabinOrder": "0", "airLine": 1, "flyType": 0, "international": "0", "action": "0", "segType": "1", "cache": 1, "preUrl": "", "isMember": "" } r = requests.post(url, data=json.dumps(param), headers={"Content-Type": "application/json"}) res = r.json() if res["success"] is True: res_data = res["data"]["segment"][0]["dateFlight"] for i in res_data["flight"]: flight_list[i["flightNo"]] = i else: self.log.add_log("Skill-FlightSearcher: Flight search fail", 3) self.log.add_log(" Error code:" + res["errorCode"], 3) self.log.add_log(" Error msg:" + res["errorMsg"], 3) self.log.add_log(" The response: \n" + str(res), 3) return flight_list def get_cities_info_list(self): cities_info_list = {} url = "https://www.csair.com/cn/scripts/city/getcitylist.html?callback=getB2CCmsCity&_=1564394534382" r = requests.get(url) content = r.content.decode().replace("getB2CCmsCity(", "").replace(")", "") res = json.loads(content) for i in res["cities"]: cities_info_list[i[0]] = [i[4], i[11]] return cities_info_list def main(self): self.tts.start("Welcome using the FlightTracer. Made by Lan_zhijiang") self.tts.start("Loading cities... please wait...") cities_info_list = self.get_cities_info_list() self.tts.start("Load finished") self.tts.start("请问你要从哪里出发?请说出城市名") self.player.ding() self.recorder.record() self.player.dong() dep = self.stt.start() self.tts.start("请问你要到哪里去?请说出城市名") self.player.ding() self.recorder.record() self.player.dong() arr = self.stt.start() date = self.log.get_date() self.tts.start("请问你要什么时候出发?月份+日期,今天是" + date) self.player.ding() self.recorder.record() self.player.dong() date = "20190811" self.log.add_log("Skill-FlightSearcher: Confirming info...", 1) self.log.add_log.tts("Information confirm completed", 1) self.log.add_log("Searching for the flight...please wait...", 1) flight_list = self.request_csairlines(dep, arr, date, cities_info_list, 1) flight_num_list = list(flight_list.keys()) for i in flight_num_list: dep_time = flight_list[i]["depTime"][0:2] + ":" + flight_list[i][ "depTime"][2:4] arr_time = flight_list[i]["arrTime"][0:2] + ":" + flight_list[i][ "arrTime"][2:4] self.tts.start(i + ",这趟航班的经济舱价格是" + str(flight_list[i]["cabin"][-1]["adultPrice"]) + ",出发时间是" + str(dep_time) + ",到达时间是" + str(arr_time))
class XiaolanSkillHass(): def __init__(self, log, setting, text): self.log = log self.setting = setting self.text = text self.stt = BaiduStt(log, setting) self.tts = BaiduTts(log, setting) self.nlu = XiaolanNlu(log, setting) self.recorder = XiaolanRecorder(log, setting) self.player = XiaolanPlayer(log, setting) self.hass_settings = setting["skillSettings"]["hass"] self.hass_addr = self.hass_settings["hassAddress"] self.hass_token = self.hass_settings["token"] self.headers = { "Authorization": "Bearer " + self.hass_token, "Content-Type": "application/json" } self.components = self.hass_settings["components"] self.unit_system = self.hass_settings["unitSystem"] self.domain_services = self.hass_settings["domainServices"] self.entity_dict = self.hass_settings["entityDict"] self.friendly_name_dict = self.hass_settings["friendlyNameDict"] self.intent_service_list = self.hass_settings["intentToServiceList"] self.nlu_intent_dict = { "更新数据": "update_data", "打开": "turn_on", "关闭": "turn_off", "颜色": "set_color", "查看": "get_state", "设置": "set", "改": "set", "获取": "get_state", "是": "yes", "否": "no", "不": "no", "退出": "quit" } def start(self): """ 开始 :return: """ self.log.add_log("XiaolanSkillHass: Initializing...", 1) self.tts.start("Hi!~ 这里是智能家居控制系统~") self.main() def conversation(self): """ 对话(减少重复) :return: """ self.player.ding() self.recorder.record() self.player.dong() text = self.stt.start() intent, slot = self.nlu.skill_nlu(text, self.nlu_intent_dict) if intent is None: self.log.add_log("XiaolanSkillHass: Intent is none, quit", 1) self.tts.start("不好意思,我没明白,退出咯~") intent, text, slot = None, None, None self.log.add_log( "XiaolanSkillHass: Intent: " + intent + " Text: " + text + " Slot: " + slot, 1) return intent, text, slot def main(self): """ 主逻辑程序(可多次对话) :return: """ self.tts.start("有何贵干呐?敬请吩咐。若不明白如何操作,请查阅本技能的wiki") intent, text, slot = self.conversation() if intent is None: return self.log.add_log("XiaolanSkillHass: Recognized intent: " + intent, 1) if intent == "update_data": self.update_data() self.tts.start("所有数据已更新") elif intent == "get_state": friendly_name = slot state = self.get_state(self.friendly_name_dict[friendly_name]) self.tts.start(friendly_name + "的状态是" + state["state"].replace("_", " ")) elif intent == "quit": self.tts.start("好的,我走啦,下次再见~") return else: service = self.intent_service_list[intent] service_path = self.domain_services[service] param = {"entity_id": self.friendly_name_dict[slot]} self.call_service_v2(service_path, param) self.tts.start("还要继续吗?") intent, text, slot = self.conversation() if intent is None: return if intent == "yes": self.main() else: self.tts.start("好的,我走啦,下次再见~") def request(self, url, type, param=None): """ 使用requests库进行get/post请求(减少重复代码) :param url: 请求的地址 :param param: 请求的数据 post才需要 :param type: 请求类型 get/post :return: """ if type == "get": r = requests.get(url, headers=self.headers) elif type == "post": r = requests.post(url, data=param, headers=self.headers) else: return None if r.status_code == 200: return r else: self.log.add_log("XiaolanSkillHass: Request " + str(r.status_code), 3) self.log.add_log(" Data: " + str(param), 1) self.log.add_log(" Res: " + str(r.text), 1) return None def update_data(self): """ 更新所有数据 :return: """ self.log.add_log("XiaolanSkillHass: Update all data...", 1) self.get_services() self.get_entity_list() self.get_config() self.hass_settings["components"] = self.components self.hass_settings["unitSystem"] = self.unit_system self.hass_settings["domainServices"] = self.domain_services self.hass_settings["entityDict"] = self.entity_dict self.hass_settings["friendlyNameDict"] = self.friendly_name_dict self.setting["hassSettings"] = self.hass_settings json.dump(self.setting, open("./data/json/setting.json", "w", encoding="utf-8")) self.log.add_log("XiaolanSkillHass: Updated", 1) def get_config(self): """ 获取hass的配置 :return: """ self.log.add_log("XiaolanSkillHass: Get config", 1) r = self.request(self.hass_addr + "/api/config", "get") if r is not None: res = r.json() self.components = res["components"] self.unit_system = res["unit_system"] def get_services(self): """ 获取所有服务 :return: """ self.log.add_log("XiaolanSkillHass: Get all services' info", 1) r = self.request(self.hass_addr + "/api/services", "get") if r is not None: res = r.json() for event in res: for service in event["services"]: self.domain_services[ service] = event["domain"] + "/" + service def get_entity_list(self): """ 获取所有对象的状态 :return: """ self.log.add_log("XiaolanSkillHass: Get all entities' info", 1) r = self.request(self.hass_addr + "/api/states", "get") if r is not None: res = r.json() for event in res: state = self.get_state(event["entity_id"]) self.entity_dict[event["entity_id"]] = { "attr": state["attributes"], "state": state["state"], "friendly_name": state["friendly_name"] } self.friendly_name_dict[ state["friendly_name"]] = event["entity_id"] def get_state(self, entity_id): """ 获取指定对象的状态 :param entity_id: 实体id :return: """ self.log.add_log("XiaolanSkillHass: Get state of" + entity_id, 1) r = self.request(self.hass_addr + "/api/states/" + entity_id, "get") if r is not None: res = r.json() if r.status_code == 404: self.log.add_log( "XiaolanSkillHass: Can't find " + entity_id + "'s state", 3) return None return res def call_service(self, domain, service, param): """ 呼叫某个服务 :param domain: 服务所属领域 :param service: 服务名 :param param: 服务参数 :return: """ self.log.add_log( "XiaolanSkillHass: Call " + domain + "/" + service + " with " + str(param), 1) r = self.request( self.hass_addr + "/api/services/" + domain + "/" + service, "post", param) if r is not None: res = r.json() for event in res: self.entity_dict[event["entity_id"]] = { "attr": event["attributes"], "state": event["state"] } return res def call_service_v2(self, service_path, param): """ 呼叫某个服务 :param service_path: 服务路径 :param param: 服务参数 :return: """ self.log.add_log( "XiaolanSkillHass: Call " + service_path + " with " + str(param), 1) r = self.request(self.hass_addr + "/api/services/" + service_path, "post", param) if r is not None: res = r.json() for event in res: self.entity_dict[event["entity_id"]] = { "attr": event["attributes"], "state": event["state"] } return res
class XiaolanInit(): def __init__(self): self.setting = json.load( open("./data/json/setting.json", "r", encoding="utf-8")) self.log = XiaolanLog() self.tts = BaiduTts(self.log, self.setting) self.stt = BaiduStt(self.log, self.setting) self.nlu = XiaolanNlu(self.log, self.setting) self.player = XiaolanPlayer(self.log, self.setting) self.recorder = XiaolanRecorder(self.log, self.setting) self.skills = XiaolanSkillsManager(self.log, self.setting) self.snowboy = XiaolanSnowboy(self.log, self.setting) def run(self): """ 启动小蓝 :return: """ print(""" ######################################## 小蓝-中文智能家居语音交互机器人 (c)Lan_zhijiang all rights reserved http://github.com/xiaoland/xiaolan E-mail: [email protected] ######################################## """) self.log.add_log("Xiaolan start! ", 1) self.log.add_log("Self checking...", 1) self.log.add_log("XiaolanInit: Play the welcome speech(online tts)", 1) self.tts.start("你好啊,主人~这里是小蓝哦!") self.log.add_log("Start pulseaudio", 1) os.system("pulseaudio --start") self.log.add_log("Run snowboy awaken engine", 1) self.snowboy.run(self.callback) def callback(self): """ 回调函数 :return: """ self.log.add_log("XiaolanInit: Detected awaken from snowboy", 1) self.conversation() def conversation(self): """ 开始对话 :return: """ self.log.add_log("XiaolanInit: Start new conversation", 1) self.player.ding() self.recorder.record() self.player.dong() text = self.stt.start() if text is None: self.log.add_log( "XiaolanInit: Text is none, play text_none(online tts)", 1) self.tts.start("抱歉,我好像没听清") return else: intent = self.nlu.get_intent(text) if intent[0] is None: self.log.add_log( "XiaolanInit: Intent is none, play intent_none(online tts)", 1) self.tts.start("我不是很明白你的意思") return else: if intent[0] == "call_skill": self.skills.skill_skills_list[intent[1]](self.log, self.setting, text).start() else: self.skills.intent_skills_list[intent[0]](self.log, self.setting, text).start()
class XiaolanSkillMusicPlayer(object): def __init__(self, log, setting, text): self.log = log self.setting = setting self.text = text self.stt = BaiduStt(log, setting) self.tts = BaiduTts(log, setting) self.nlu = XiaolanNlu(log, setting) self.player = XiaolanPlayer(log, setting) self.recorder = XiaolanRecorder(log, setting) self.play_list = {} self.nlu_dict = { "恢复": "resume", "播放": "play", "暂停": "pause", "下一首": "next", "上一首": "previous", "添加": "add", "退出": "quit" } def start(self): """ 启动 :return: """ self.log.add_log("XiaolanSkillMusicPlayer: Start", 1) self.tts.start("请问你要听什么音乐呢?") self.main() def conversation(self): """ 对话(减少重复代码) :return: """ self.player.ding() self.recorder.record() self.player.dong() text = self.stt.start() intent, slot = self.nlu.skill_nlu(text, self.nlu_dict) if intent is None: self.log.add_log("XiaolanSkillMusicPlayer: Intent is none, quit", 1) self.tts.start("不好意思,我没明白,退出咯~") intent, text, slot = None, None, None self.log.add_log( "XiaolanSkillMusicPlayer: Intent: " + intent + " Text: " + text + " Slot: " + slot, 1) return intent, text, slot def main(self): """ 主业务逻辑 :return: """ intent, text, slot = self.conversation() if intent == "quit": self.tts.start("好的,那么bye~") return elif intent == "play": result = self.search(slot) if result is None: self.tts.start("没有找到你要找的曲目,你可以试着换个说法") self.main() else: self.play(result) else: pass # NOT FINISHED self.tts.start("还要继续吗?") intent, text, slot = self.conversation() if intent is None: return if intent == "yes": self.main() else: self.tts.start("好的,我走啦,下次再见~") def play(self, data): """ 播放 :param data: 播放参数 :return: """ self.log.add_log("XiaolanSkillMusicPlayer: Start play...", 1) url = "https://wwwapi.kugou.com/yy/index.php?" param = {"r": "play/getdata", "hash": data["fileHash"]} param = urllib.parse.urlencode(param) r = requests.get(url + param) if r.status_code == 200: res = r.json() play_url = res["data"]["play_url"].replace("\\\\", "") self.log.add_log("XiaolanSkillMusicPlayer: Play url: " + play_url, 1) r2 = requests.get(play_url) file = open("./data/audio/music" + data["fileFormat"], "wb") file.write(r2.content) file.close() self.player.format_converter( "/data/audio/music" + data["fileFormat"], data["fileFormat"], ".wav") self.player.play("./data/audio/music.wav") else: self.log.add_log( "XiaolanSkillMusicPlayer: Can't get: " + data["fileHash"] + " 's play url. Http error", 3) return None def search(self, keyword): """ 搜索曲目 :param keyword: 关键词 :return: """ self.log.add_log( "XiaolanSkillMusicPlayer: Search for song named: " + keyword, 1) url = "https://complexsearch.kugou.com/v2/search/song?" param = { "callback": "callback123", "keyword": keyword, "page": 1, "pagesize": 30, "bitrate": 0, "isfuzzy": 0, "tag": "em", "inputtype": 0, "platform": "WebFilter", "userid": -1, "clientver": 2000, "iscorrection": 1, "privilege_filter": 0, "srcappid": 2919, "clienttime": 1601950702328, "mid": 1601950702328, "uuid": 1601950702328, "dfid": "-", "signature": "10C3F1C841892B24FD43DE172EB765EA" } param = urllib.parse.urlencode(param) r = requests.get(url + param) if r.status_code == 200: res = r.json() data = res["data"]["lists"] file_format = "." + data[0]["HQExtName"] song_name = data[0]["SongName"].replace("em>", "").replace( "<", "").replace("/", "") singer_name = data[0]["SingerName"] file_hash = data[0]["HQFileHash"] self.log.add_log( "XiaolanSkillMusicPlayer: song: " + song_name + " singer: " + singer_name + " file hash: " + file_hash, 1) return { "fileFormat": file_format, "fileHash": file_hash, "songName": song_name, "singerName": singer_name } else: self.log.add_log( "XiaolanSkillMusicPlayer: Search: " + keyword + " failed. Http error", 3) return None