Example #1
0
    def on_response(self, msg):
        """
        応答の受信

        :param dict msg: dictionary converted from json
         str  topic : raspberrypi/response
         int  qos :
         json payload : {"param1": "...", "param2": "..."}

          payload : {"action": "...", "request_id": "..."}
        	:str action: requestで指定したaction
        	:str request_id: requestで指定したrequest_id
        """
        try:
            self.logger.info("Topic: " + str(msg.topic))
            self.logger.info("QoS: " + str(msg.qos))
            self.logger.info("Payload: " + str(msg.payload))

            # topic 確認
            # Level1:既定文字列のチェック
            levels_pub = msg.topic.split('/', 2)
            levels_sub = self.topic_sub.split('/')
            if levels_pub[0] != levels_sub[0]:
                raise ParamError("invalid topic.")

            # Level2:typeのチェック
            if levels_pub[1] != levels_sub[1]:
                raise ParamError("invalid type.")

            # responseをjsonデコード
            response = json.loads(msg.payload)

            # リクエストIDのチェック
            if not response['request_id']:
                raise ParamError("can't find request_id.")
            if response['request_id'] != self.request_id:
                raise ParamError("invalid request_id.")

            # 処理終了
            self.response = response
            self.isReceived = True
            self.logger.info('received valid response.')

        except Error as e:
            self.logger.error(e.description)
            # 正しい応答を待つ

        except Exception as e:
            self.logger.critical(e)
            self.logger.critical(util.trace())
def lambda_handler(event, context):
    """
    Lambda Handler
    
    :param str event['sender']: (required) 'slack' or 'raspberrypi'
    :param str event['action']: (required) RaspberryPiに指示するコマンド
    :param str event['user']: (optional) 指示ユーザ
    
     sender:
      slack --
       action:
        speak --
         :param str event['text'] : (required) 再生するメッセージ

      raspberrypi --
       action:
        listend --
         :param str event['result'] : (required) 音声聞き取りの結果
    """

    try:
        # ルートハンドラ除去&ロギング設定
        _lambda.util.basicConfig()
        logger = logging.getLogger(__file__)
        logger.info(event)

        # API Gateway(Slack) or Aws IoT(RaspberryPi)
        if 'sender' not in event:
            raise ParamError("senderが指定されていません。")
        else:
            sender = event['sender']

    except Exception as exp:
        logger.critical(exp)
        logger.critical(util.trace())
        raise exp

    try:
        # 呼び出し元によって分岐
        if sender == 'raspberrypi':
            # パラメータを取得
            if 'action' not in event:
                raise ParamError("actionが指定されていません。")
            action = event['action']

            # actionに従った処理
            if action == 'listened': 
                # パラメータ準備
                if 'result' not in event:
                    raise ParamError("resultが指定されていません。")
                text = event['result']

                # Lambda設定の確認    
                if 'SLACK_WEBHOOK_URL' not in os.environ:
                    raise InternalError("SLACK_WEBHOOK_URLが設定されていません。")

                # Slackへ通知
                slack = IncomingWebHooks(
                    url = os.environ['SLACK_WEBHOOK_URL'],
                    logging = logging
                )
                slack.webhook(text)
    
            else:
                raise ParamError("定義されていないactionです。")
            
            return 'success'
            
        else:
            # パラメータを取得
            if 'action' not in event:
                raise HttpParamError("actionが指定されていません。")
            action = event['action']

            user = None
            if 'user' in event:
                user = event['user']
    
            # actionに従った処理
            if action == 'speak': 
                # パラメータ準備
                if 'text' not in event:
                    raise HttpParamError("textが指定されていません。")
                text = event['text']

                # テキスト処理
                ## リマインダ用のprefixを削除
                text = re.sub(r"^Reminder: ", "", text)

                # 音声出力処理
                cwd = os.getcwd()
                pi = RaspberryPi(
                    host = os.environ['SUBSCRIBE_HOST'] if 'SUBSCRIBE_HOST' in os.environ else None,
                    ca = cwd + '/' + os.environ['SUBSCRIBE_CAROOTFILE'] if 'SUBSCRIBE_CAROOTFILE' in os.environ else None,
                    cert = cwd + '/' + os.environ['SUBSCRIBE_CERTFILE'] if 'SUBSCRIBE_CERTFILE' in os.environ else None,
                    key = cwd + '/' + os.environ['SUBSCRIBE_KEYFILE'] if 'SUBSCRIBE_KEYFILE' in os.environ else None,
                    logging = logging
                )
                result = pi.speak(text)
                logger.info(result)
    
            else:
                raise HttpParamError("定義されていないactionです。")
    
            # 正常終了
            return apigateway.success(result + "invoked %s" % (user))

    except Error as err:
        logger.error(err)
        e = err

    except Exception as exp:
        logger.critical(exp)
        logger.critical(util.trace())
        e = HttpInternalError("内部エラーが発生しました。")

    # 共通エラー処理(呼び元に従う)
    if sender == 'raspberrypi':
        # 通常のエラー to AWS IoT / InvocationType : Event
        raise e
    else:
        # 200としてエラー to API Gateway /InvocationType : RequestResponse
        return apigateway.error(e.statusCode, e.error, e.description)
Example #3
0
def run(is_daemon = False):
	AwsIoTSpeaker(
		file_config = 'config.ini',
		is_daemon = is_daemon
	).run()

def daemonize():
	pid = os.fork()
	if pid > 0:
		f = open('/var/run/aws-iot-speakerd.pid', 'w')
		f.write(str(pid) + "\n")
		f.close()
		sys.exit()

	elif pid == 0:
		run(True)

if __name__== '__main__':
	try:
		# コマンドライン引数を判定
		if '-D' in sys.argv:
			# デーモン起動
			daemonize()
		else:
			# 通常起動
			run(False)

	except Exception as e:
		print(e)
		print(util.trace())
Example #4
0
	def _on_message(self, mosq, obj, msg):
		"""
		:param dict msg: dictionary converted from json
		 str  topic : raspberrypi/request/{action}
		 int  qos :
		 json payload : {"param1": "...", "param2": "..."}

		  action :
		   speak -- payload : {"text": "...", "voice": "..."}
			:str text: 再生するテキスト
			:str voice: "Takumi" or "Mizuki"
		"""
		try:
			self.logger.info("Topic: " + str(msg.topic))
			self.logger.info("QoS: " + str(msg.qos))
			self.logger.info("Payload: " + str(msg.payload))

			ack = {}

			# topic 確認
			# Level1:既定文字列のチェック
			levels_pub = msg.topic.split('/', 2)
			levels_sub = self.topic_sub.split('/')
			if levels_pub[0] != levels_sub[0]:
				raise ParamError("invalid topic.")

			# Level2:typeのチェック
			if levels_pub[1] != levels_sub[1]:
				raise ParamError("invalid type.")

			# Level3:actionのチェックと取得
			if len(levels_pub) < 3 :
				raise ParamError("can't find action.")
			action = levels_pub[2]

			# レスポンス
			ack['action'] = action

			"""
			# 一応下位を取得している
			subtopics = None
			if len(topics_pub) > 2 :
				subtopics = topics_pub[2].split('/')
			"""

			# パラメータをjsonデコード
			param = json.loads(msg.payload)

			# リクエストIDをチェック、ACKに設定
			if not param['request_id']:
				raise ParamError("can't find request_id.")
			ack['request_id'] = param['request_id']

			# action毎の処理
			if action == 'speak':
				self.speaker.play(param)
				ack['result'] = '指定されたテキストを読み上げました。'
			else:
				raise ParamError("can't find action.")

			# 処理終了
			self.logger.info('success')

		except ParamError as e:
			# 他のメッセージを処理しない
			self.logger.info(e.description)
			return

		except Error as e:
			self.logger.error(e.description)
			ack['error'] = e.error
			ack['error_description'] = e.description

		except Exception as e:
			self.logger.critical(e)
			self.logger.critical(util.trace())

			ack['error'] = 'internal_error'
			ack['error_description'] = str(e)

		# ACK返却
		self.publish(self.topic_pub, **ack)
		self.logger.info('on message complete.')
Example #5
0
    def _on_message(self, mosq, obj, msg):
        """
		:param dict msg: dictionary converted from json
		 str  topic : raspberrypi/request/{action}
		 int  qos :
		 json payload : {"param1": "...", "param2": "..."}

		  action :
		   listen -- payload : null
		"""
        try:
            self.logger.info("Topic: " + str(msg.topic))
            self.logger.info("QoS: " + str(msg.qos))
            self.logger.info("Payload: " + str(msg.payload))

            ack = {}
            ack['sender'] = 'raspberrypi'

            # topic 確認
            # Level1:既定文字列のチェック
            levels_pub = msg.topic.split('/', 2)
            levels_sub = self.topic_sub.split('/')
            if levels_pub[0] != levels_sub[0]:
                raise ParamError("invalid topic.")

            # Level2:typeのチェック
            if levels_pub[1] != levels_sub[1]:
                raise ParamError("invalid type.")

            # Level3:actionのチェックと取得
            if len(levels_pub) < 3:
                raise ParamError("can't find action.")
            action = levels_pub[2]

            # action毎の処理
            if action == 'listen':
                ack['action'] = 'listened'

                # 音声録音とテキスト変換
                result = self.listener.listen()
                ack['result'] = result
            else:
                raise ParamError("can't find action.")

            # 処理終了
            self.logger.info('on message success.')

        except ParamError as e:
            # 他のメッセージを処理しない
            self.logger.info(e.description)
            return

        except RecognitionError as e:
            # 通常変換失敗時
            self.logger.error(e.description)
            ack['result'] = "音声の聞き取りに失敗しました。"

        except Error as e:
            # その他ユーザエラー全般
            self.logger.error(e.description)
            ack['result'] = "録音処理中にエラーが発生しました。"

        except Exception as e:
            # その他エラー全般
            self.logger.critical(e)
            self.logger.critical(util.trace())
            ack['result'] = "録音処理中にエラーが発生しました。"

        # 応答返却
        self.publish(self.topic_pub, **ack)
        self.logger.info('on message complete.')
Example #6
0
    def _record(self):
        chunk = 1024 * 20
        #サンプリングレート、マイク性能に依存
        RATE = 44100
        #録音時間
        RECORD_SECONDS = 10

        #マイク番号を設定: cat /proc/asound/modules
        input_device_index = self.input_device_index
        #マイクからデータ取得
        stream = self.audio.open(format=pyaudio.paInt16,
                                 channels=1,
                                 rate=RATE,
                                 input=True,
                                 frames_per_buffer=chunk)
        try:
            # 録音処理
            sec_per_buffer = RATE / chunk
            SEC_STOP = 1.5
            threshold_start = 0.002  # 何か音が有ったら開始
            threshold_end = 0.002  # 音がある程度無くなったら終了
            no_sound = 0
            recording = False
            all = []

            self.logger.info("recording...")
            for i in range(0, sec_per_buffer * RECORD_SECONDS):
                data = stream.read(chunk, False)

                # @refs https://qiita.com/mix_dvd/items/dc53926b83a9529876f7
                # 最大1に正規化
                x = np.frombuffer(data, dtype="int16") / 32768.0
                # 正負に伸びる、バラつきあるので平均化
                ave = np.average(np.absolute(x))
                #				self.logger.info(str(i))
                #				self.logger.info("x(cnt): " + str(len(x)))
                self.logger.info("x(ave): " + str(ave))
                #				self.logger.info("x(max): " + str(x.max()))
                #				self.logger.info("x(min): " + str(x.min()))
                if not recording:
                    if ave > threshold_start:
                        recording = True
                else:
                    # 録音データを取得
                    all.append(data)

                    # 無音が暫く続いたら終了
                    if ave < threshold_end:
                        no_sound += 1
                        if no_sound >= (sec_per_buffer * SEC_STOP):
                            break
                    else:
                        no_sound = 0

            stream.close()
            self.logger.info("recorded.")

        except Exception as e:
            stream.close()

            self.logger.critical(e)
            self.logger.critical(util.trace())
            raise e

        ## ファイルが存在した場合は削除
        if os.path.isfile(self.path_voice):
            os.remove(self.path_voice)

        # 書き込み
        out = wave.open(self.path_voice, 'w')
        try:
            out.setnchannels(1)  #mono
            out.setsampwidth(2)  #16bits
            out.setframerate(RATE)
            out.writeframes(''.join(all))
            out.close()

        except Exception as e:
            out.close()

            self.logger.critical(e)
            self.logger.critical(util.trace())
            raise e