コード例 #1
0
ファイル: env_sensor.py プロジェクト: cnaos/raspi-wxbeacon2
class OmronEnvSensor:
    @retry(tries=3, delay=1, logger=logger)
    def __init__(self, address: str):
        super().__init__()
        self.address = address
        self.ble_peripheral = Peripheral(address,
                                         addrType=bluepy.btle.ADDR_TYPE_RANDOM)
        self.ble_peripheral.discoverServices()

    @property
    def peripheral(self):
        return self.ble_peripheral

    # @retry(tries=5, delay=1, logger=logger)
    def read_char_base(self, service_uuid,
                       char_uuid) -> (btle.Characteristic, bytes):
        """
        BLEのCharacteristicsを読む

        Args:
            service_uuid: Omron環境センサBLEサービスのshort UUID
            char_uuid: Omron環境センサBLE Characteristicsのshort UUID

        Returns:
            (btle.Characteristic, bytes):
        """
        time.sleep(BLE_READ_CHARA_WAIT_SEC)
        service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid)
        ble_char = service.getCharacteristics(forUUID=char_uuid)[0]

        time.sleep(BLE_READ_WAIT_SEC)
        raw_data = ble_char.read()
        return ble_char, raw_data

    # @retry(tries=5, delay=1, logger=logger)
    def write_char_base(self, service_uuid, char_uuid,
                        write_value: bytes) -> None:
        """
        BLEのCharacteristicsに値を書く

        Args:
            service_uuid: Omron環境センサBLEサービスのshort UUID
            char_uuid: Omron環境センサBLE Characteristicsのshort UUID
            write_value: 書き込む値

        Returns:
            None
        """

        time.sleep(BLE_READ_CHARA_WAIT_SEC)
        service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid)
        ble_char = service.getCharacteristics(forUUID=char_uuid)[0]

        time.sleep(BLE_WRITE_WAIT_SEC)
        ble_char.write(write_value)

    def __MSG(self, level: int, *args) -> None:
        """
        Bluetoothデバイスのアドレスをつけてログ出力する

        Args:
            level: ログ出力レベル
            *args: ログ出力内容

        Returns:
            None
        """

        msg = " ".join([str(a) for a in args])
        logger.log(level, F'{self.address}: {msg}')

    def read_device_name(self) -> str:
        """
        BLEデバイスの名前を読む(Generic Access:0x1800, Device Name:0x2a00)

        Returns:
            str: BLEデバイスの名前
        """
        (ble_chara, raw_data) = self.read_char_base(0x1800, 0x2a00)
        str_device_name = raw_data.decode('utf-8')
        self.__MSG(
            DEBUG,
            f'char={ble_chara}, raw_data={raw_data}, str={str_device_name}')
        return str_device_name

    def check_omron_env_sensor(self) -> bool:
        """
        デバイスがOmronの環境センサーかどうかを判定する

        Returns:
            bool: TrueならOmronの環境センサー

        """
        str_device_name = self.read_device_name()
        if str_device_name == "EnvSensor-BL01":
            return True
        else:
            self.__MSG(WARN, f'this device is not OMRON ')
            return False

    def read_latest_data(self) -> OmronLatestData:
        """
        環境センサーのLatest Dataを読み出す

        Returns:
            LatestData
        """

        latest_data = OmronLatestData()

        self.__MSG(DEBUG,
                   F'reading LatestData(uuid={latest_data.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(latest_data.serviceUuid,
                                                    latest_data.uuid)
        return latest_data.parse(raw_data)

    def read_latest_page(self) -> OmronLatestPage:
        """
        環境センサーのLatest Pageを読み出す

        Returns:
            LatestPage
        """

        latest_page = OmronLatestPage()

        self.__MSG(DEBUG,
                   F'reading LatestPage(uuid={latest_page.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(latest_page.serviceUuid,
                                                    latest_page.uuid)
        return latest_page.parse(raw_data)

    def write_request_page(self, page: int, row: int) -> None:
        """
        環境センサーへ読み出したいページのRequest Page要求を出す

        Args:
            page: 読み出したいページ番号 0から2047
            row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい

        Returns:
            None
        """
        assert 0 <= page <= 2047
        assert 0 <= row <= 12
        request_page = OmronRequestPage()
        data = request_page.encode_data(page, row)

        self.__MSG(
            DEBUG,
            F'writing RequestPage(uuid={request_page.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(request_page.serviceUuid, request_page.uuid, data)

    def read_response_flag(self) -> OmronResponseFlag:
        """
        環境センサーのResponse Flagを読み出す。

        Returns:
            ResponseFlag
        """
        response_flag = OmronResponseFlag()

        self.__MSG(
            DEBUG,
            F'reading ResponseFlag(uuid={response_flag.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(response_flag.serviceUuid,
                                                    response_flag.uuid)
        return response_flag.parse(raw_data)

    def read_response_data(self) -> OmronResponseData:
        """
        環境センサーのResponse Dataを読み出す

        Returns:
            ResponseData
        """
        response_data = OmronResponseData()

        self.__MSG(
            DEBUG,
            F'reading ResponseData(uuid={response_data.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(response_data.serviceUuid,
                                                    response_data.uuid)
        return response_data.parse(raw_data)

    def read_time_information(self) -> OmronTimeInformation:
        """
        環境センサーのTime Informationを読み出す
        Returns:
            TimeInformation
        """

        time_information = OmronTimeInformation()

        self.__MSG(
            DEBUG,
            F'reading TimeInformation(uuid={time_information.shortUuid:#04x})')
        (ble_chara,
         raw_data) = self.read_char_base(time_information.serviceUuid,
                                         time_information.uuid)
        return time_information.parse(raw_data)

    def read_measurement_interval(self) -> OmronMeasurementInterval:
        """
        環境センサーのMeasurement Intervalを読み出す

        Returns:
            MeasurementInterval
        """
        measurement_interval = OmronMeasurementInterval()

        self.__MSG(
            DEBUG,
            F'reading MeasurementInterval(uuid={measurement_interval.shortUuid:#04x})'
        )
        (ble_chara,
         raw_data) = self.read_char_base(measurement_interval.serviceUuid,
                                         measurement_interval.uuid)
        return measurement_interval.parse(raw_data)

    def read_error_status(self) -> OmronErrorStatus:
        """
        環境センサーのError Statusを読み出す

        Returns:
            ErrorStatus
        """
        error_status = OmronErrorStatus()

        self.__MSG(DEBUG,
                   F'reading ErrorStatus(uuid={error_status.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(error_status.serviceUuid,
                                                    error_status.uuid)
        return error_status.parse(raw_data)

    # @retry(tries=3, delay=1, logger=logger)
    def write_read_page_and_wait_ready(self, page: int,
                                       row: int) -> OmronResponseFlag:
        """
        環境センサーに読み出したいページを指定して、準備が完了するまで待つ

        Args:
            page: 読み出したいページ番号 0から2047
            row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい

        Returns:
            ResponseFlag

        """
        assert 0 <= page <= 2047
        assert 0 <= row <= 12
        self.write_request_page(page, row)
        for i in range(3):
            response_flag = self.read_response_flag()
            self.__MSG(DEBUG, F'response_flag={response_flag}')
            if response_flag.update_flag == 0x01:  # 更新完了
                return response_flag
            elif response_flag.update_flag == 0x00:  # 更新中
                continue
            else:  # 更新失敗
                self.__MSG(ERROR, F'response flag failed.')
                raise IOError
        self.__MSG(ERROR, F'read response flag failed after retry.')
        raise IOError

    # @retry(tries=3, delay=1, logger=logger)
    def read_env_sensor_data(self, page: int,
                             latest_page: OmronLatestPage) -> List[LogData]:
        """
        環境センサーの指定ページを読み出す

        Args:
            page: 読み出し対象ページ
            latest_page: 環境センサーのLatestPage、最新ページの読み出し行数の指定と観測データの時刻計算にmeasurement_intervalを使う

        Returns:
            List[LogData]: 読みだした観測データのリスト
        """
        try:
            target_row = 12 if page != latest_page.page else latest_page.row  # 最新ページ以外は13行の読み出し

            response_flag = self.write_read_page_and_wait_ready(
                page, target_row)
            self.__MSG(
                INFO,
                f'page = {page}, start_time={response_flag.datetime} ({response_flag.unix_time})'
            )

            page_data = []
            for i in range(13 + 3):  # 1ページ分のデータは最大13件だけど、予備で3回追加しておく
                response_data = self.read_response_data()
                self.__MSG(DEBUG, F'response_data = {response_data}')
                page_data.append(response_data)
                if response_data is None or response_data.row == 0:
                    break

            list_log_data = list(
                map(
                    lambda it:
                    LogData(self.address, page, response_flag.unix_time,
                            latest_page.measurement_interval, it), page_data))

            self.__MSG(
                DEBUG,
                F'list_log_data={json.dumps(list_log_data, cls=DateTimeSupportJSONEncoder)}'
            )
            return list_log_data
        except Exception as e:
            logger.exception(f"read_env_sensor_data failed: {e}")
            raise e

    def write_measurement_interval(self,
                                   new_measurement_interval: int) -> None:
        """
        測定間隔を変更する

        Args:
            new_measurement_interval: 測定間隔(秒) 1から3600

        Returns:
            None
        """
        assert 1 <= new_measurement_interval <= 3600
        measurement_interval = OmronMeasurementInterval()
        data = measurement_interval.encode_data(new_measurement_interval)

        self.__MSG(
            DEBUG,
            F'writing MeasurementInterval(uuid={measurement_interval.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(measurement_interval.serviceUuid,
                             measurement_interval.uuid, data)

    def write_time_information(self, unix_time) -> None:
        """
        環境センサーへ現在時刻を設定する

        Args:
            unix_time: 現在時刻のunixタイムスタンプ

        Returns:
            None
        """
        time_information = OmronTimeInformation()
        data = time_information.encode_data(unix_time)

        self.__MSG(
            DEBUG,
            F'writing TimeInformation(uuid={time_information.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(time_information.serviceUuid,
                             time_information.uuid, data)
コード例 #2
0
    global de
    for u, s in de.services.items():
        if u == uuid:
            return s
    print('no such service uuid: %s' % uuid)
    return None

def lsc(serv):
    for c in serv.getCharacteristics():
        print('handle=%-3s valHandle=%-3s supportsRead=%-5s uuid=%s properties=%s' % (
                c.handle,
                c.valHandle,
                c.supportsRead(),
                str(c.uuid),
                c.propertiesToString()))

def handler(handle, data):
    print('handler(handle=%s, data=%s)' % (repr(handle), repr(data)))
    
print('connecting to %s...' % addr)
de = Peripheral(addr)
print('connected, discovering services...')
de.discoverServices()
print('commands:')
print('  lss()            list services')
print('  service(uuid)    get service object')
print('  lsc(service)     list characteristics for a service')
print('  char(serv, uuid) get a characteristic by uuid')


コード例 #3
0
ファイル: notify.py プロジェクト: matthewg42/fire_pong
ledev = None

def service(uuid):
    global ledev
    for u, s in ledev.services.items():
        print('%s : %s' % (str(u), str(s)))
        if u == uuid:
            print('Found %s' % uuid)
            return s
    print('%s not found' % uuid)
    return None

def handler(h, d):
    print('handler(h, d), h=%s, d=%s' % (repr(h), repr(d)))

ledev = Peripheral(addr)
ledev.discoverServices()
print('device:', ledev)
#for uuid in ['fff0', 'fff3']:
#    s = service(uuid)
#    for h in range(s.hndStart, s.hndEnd+1):
#        ledev.writeCharacteristic(h, struct.pack('<BB', 0, 1))

s = service('fff0')

print('listening for notifications...')
while True:
    print(ledev.waitForNotifications(1.))


コード例 #4
0
ファイル: beepmonster.py プロジェクト: matthewg42/btlefun
class BeepMaker:
    ''' BeepMaker takes an address, queries to see if the device is beepable, and sets it beeping if it is... '''
    def __init__(self, addr):
        self.addr = addr
        self.thread = threading.Thread(target=self.run)
        self.terminate = False
        self.per = None
        self.lastBeep = 0
        self.state = 'Initialized'
        self.beep_characteristic = None

    def __str__(self):
        return 'BeepMaker[addr=%s] state=%s' % (self.addr, self.state)

    def run(self):
        try:
            self.connect()
            if not self.terminate:
                self.discover()
            if not self.terminate:
                self.verify()
            while not self.terminate:
                self.state = 'Lurking'
                if time.time() - self.lastBeep > 15:
                    self.beep()
                time.sleep(1)
        except Exception as e:
            log.exception('BeepMaker[addr=%s] %s: %s' % (self.addr, type(e), e))
            self.terminate = True

        log.debug('BeepMaker addr=%s %s' % (self.addr, 'end' if not self.terminate else 'terminated'))

    def connect(self):
        log.debug('BeepMaker[addr=%s], connecting...' % self.addr)
        self.state = 'Connecting'
        self.per = Peripheral(self.addr)

    def discover(self):
        log.debug('BeepMaker[addr=%s], service discovery...' % self.addr)
        self.state = 'Discovery'
        self.per.discoverServices()

    def verify(self):
        ''' Check this is in fact a beepable device '''
        log.debug('BeepMaker[addr=%s], analysing services...' % self.addr)
        self.state = 'Analysing'
        inf = dict()
        for uuid, service in self.per.services.items():
            if uuid == '180a':
                for c in service.getCharacteristics():
                    if c.supportsRead():
                        inf[c.uuid.getCommonName()] = c.read()
            if uuid == 'fff0':
                for c in service.getCharacteristics():
                    if c.uuid == 'fff2':
                        self.beep_characteristic = c
        if inf['Manufacturer Name String'] != b'SIGNAL' or inf['Model Number String'] != b'BT A8105':
            raise Exception('Manufacturer and/or model not a known beeper: %s' % inf)
        if self.beep_characteristic is None:
            raise Exception('No beep characteristic fff0/fff2')

    def beep(self):
        log.debug('BeepMaker[addr=%s], beeping...' % self.addr)
        self.state = 'Beeping'
        self.morse('hi mouse')
        self.lastBeep = time.time()

    def morse(self, message):
        log.debug('BeepMaker[addr=%s], morse(%s)' % (self.addr, message))
        morse = ''
        for letter in list(message):
            morse += internationalMorse[letter.upper()] + ' '
        log.debug('Morse is: %s' % morse)
        ml = list(morse)
        while len(ml) > 0:
            chunk = ml.pop(0)
            while len(ml) > 0 and ml[0] == chunk[-1]:
                chunk += ml.pop(0)
            if chunk[0] == '.':
                self.dit(len(chunk))
            elif chunk[0] == '-':
                self.dash(len(chunk))
            elif chunk[0] == ' ':
                time.sleep(0.200)
            elif chunk[0] == '/':
                time.sleep(0.400)

    def dit(self, n=1):
        self.multibeep(120, 120, n)

    def dash(self, n=1):
        self.multibeep(255, 120, n)

    def multibeep(self, ontime, offtime, n=1):
        log.debug('BeepMaker[addr=%s], multibeep(on=%s, off=%s, n=%s)' % (self.addr, ontime, offtime, n))
        sleepms = (ontime+offtime)*n+(offtime*2)
        self.beep_characteristic.write(struct.pack('BBBBB', 0xaa, 3, n, ontime, offtime))
        time.sleep(sleepms/1000)
        log.debug('BeepMaker[addr=%s], multibeep wait end' % self.addr)

    def shutdown(self):
        self.terminate = True
コード例 #5
0
ファイル: notify.py プロジェクト: matthewg42/fire_pong

def service(uuid):
    global ledev
    for u, s in ledev.services.items():
        print('%s : %s' % (str(u), str(s)))
        if u == uuid:
            print('Found %s' % uuid)
            return s
    print('%s not found' % uuid)
    return None


def handler(h, d):
    print('handler(h, d), h=%s, d=%s' % (repr(h), repr(d)))


ledev = Peripheral(addr)
ledev.discoverServices()
print('device:', ledev)
#for uuid in ['fff0', 'fff3']:
#    s = service(uuid)
#    for h in range(s.hndStart, s.hndEnd+1):
#        ledev.writeCharacteristic(h, struct.pack('<BB', 0, 1))

s = service('fff0')

print('listening for notifications...')
while True:
    print(ledev.waitForNotifications(1.))