示例#1
0
    def test_reading_broken_yaml_config(self):
        """Test when known devices contains invalid data."""
        with tempfile.NamedTemporaryFile() as fp:
            # file is empty
            assert device_tracker.load_config(fp.name, None, False, 0) == []

            fp.write('100'.encode('utf-8'))
            fp.flush()

            # file contains a non-dict format
            assert device_tracker.load_config(fp.name, None, False, 0) == []
示例#2
0
 def test_reading_broken_yaml_config(self):  # pylint: disable=no-self-use
     """Test when known devices contains invalid data."""
     files = {'empty.yaml': '',
              'bad.yaml': '100',
              'ok.yaml': 'my_device:\n  name: Device'}
     with patch_yaml_files(files):
         # File is empty
         assert device_tracker.load_config('empty.yaml', None, False) == []
         # File contains a non-dict format
         assert device_tracker.load_config('bad.yaml', None, False) == []
         # A file that works fine
         assert len(device_tracker.load_config('ok.yaml', None, False)) == 1
    def test_not_allow_invalid_dev_id(self):  # pylint: disable=invalid-name
        """Test that the device tracker will not allow invalid dev ids."""
        self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, TEST_PLATFORM))

        device_tracker.see(self.hass, dev_id="hello-world")

        config = device_tracker.load_config(self.yaml_devices, self.hass, timedelta(seconds=0))
        assert len(config) == 0
示例#4
0
    def test_not_allow_invalid_dev_id(self):
        """Test that the device tracker will not allow invalid dev ids."""
        self.assertTrue(device_tracker.setup(self.hass, {}))

        device_tracker.see(self.hass, dev_id='hello-world')

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0), 0)
        assert len(config) == 0
 def test_adding_unknown_device_to_config(self):
     scanner = get_component('device_tracker.test').SCANNER
     scanner.reset()
     scanner.come_home('DEV1')
     self.assertTrue(device_tracker.setup(self.hass, {
         device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}}))
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         timedelta(seconds=0))[0]
     self.assertEqual('dev1', config.dev_id)
     self.assertEqual(True, config.track)
示例#6
0
 def test_picture_and_icon_on_see_discovery(self):
     """Test that picture and icon are set in initial see."""
     tracker = device_tracker.DeviceTracker(
         self.hass, timedelta(seconds=60), False, {}, [])
     tracker.see(dev_id=11, picture='pic_url', icon='mdi:icon')
     self.hass.block_till_done()
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         timedelta(seconds=0))
     assert len(config) == 1
     assert config[0].icon == 'mdi:icon'
     assert config[0].entity_picture == 'pic_url'
示例#7
0
 def test_default_hide_if_away_is_used(self):
     """Test that default track_new is used."""
     tracker = device_tracker.DeviceTracker(
         self.hass, timedelta(seconds=60), False,
         {device_tracker.CONF_AWAY_HIDE: True}, [])
     tracker.see(dev_id=12)
     self.hass.block_till_done()
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         timedelta(seconds=0))
     assert len(config) == 1
     self.assertTrue(config[0].hidden)
示例#8
0
    def test_not_allow_invalid_dev_id(self):
        """Test that the device tracker will not allow invalid dev ids."""
        with assert_setup_component(1, device_tracker.DOMAIN):
            assert setup_component(self.hass, device_tracker.DOMAIN,
                                   TEST_PLATFORM)

        device_tracker.see(self.hass, dev_id='hello-world')

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert len(config) == 0
示例#9
0
    def test_adding_unknown_device_to_config(self):
        scanner = get_component('device_tracker.test').SCANNER
        scanner.reset()
        scanner.come_home('DEV1')

        self.assertTrue(device_tracker.setup(self.hass, {
            device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}}))
        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0), 0)
        assert len(config) == 1
        assert config[0].dev_id == 'dev1'
        assert config[0].track
示例#10
0
    def test_not_write_duplicate_yaml_keys(self):
        """Test that the device tracker will not generate invalid YAML."""
        self.assertTrue(device_tracker.setup(self.hass, {}))

        device_tracker.see(self.hass, 'mac_1', host_name='hello')
        device_tracker.see(self.hass, 'mac_2', host_name='hello')

        self.hass.pool.block_till_done()

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0), 0)
        assert len(config) == 2
示例#11
0
    def test_reading_broken_yaml_config(self):  # pylint: disable=no-self-use
        """Test when known devices contains invalid data."""
        files = {
            "empty.yaml": "",
            "nodict.yaml": "100",
            "badkey.yaml": "@:\n  name: Device",
            "noname.yaml": "my_device:\n",
            "allok.yaml": "My Device:\n  name: Device",
            "oneok.yaml": ("My Device!:\n  name: Device\n" "bad_device:\n  nme: Device"),
        }
        args = {"hass": self.hass, "consider_home": timedelta(seconds=60)}
        with patch_yaml_files(files):
            assert device_tracker.load_config("empty.yaml", **args) == []
            assert device_tracker.load_config("nodict.yaml", **args) == []
            assert device_tracker.load_config("noname.yaml", **args) == []
            assert device_tracker.load_config("badkey.yaml", **args) == []

            res = device_tracker.load_config("allok.yaml", **args)
            assert len(res) == 1
            assert res[0].name == "Device"
            assert res[0].dev_id == "my_device"

            res = device_tracker.load_config("oneok.yaml", **args)
            assert len(res) == 1
            assert res[0].name == "Device"
            assert res[0].dev_id == "my_device"
示例#12
0
    def test_reading_broken_yaml_config(self):
        """Test when known devices contains invalid data."""
        files = {'empty.yaml': '',
                 'nodict.yaml': '100',
                 'badkey.yaml': '@:\n  name: Device',
                 'noname.yaml': 'my_device:\n',
                 'allok.yaml':  'My Device:\n  name: Device',
                 'oneok.yaml':  ('My Device!:\n  name: Device\n'
                                 'bad_device:\n  nme: Device')}
        args = {'hass': self.hass, 'consider_home': timedelta(seconds=60)}
        with patch_yaml_files(files):
            assert device_tracker.load_config('empty.yaml', **args) == []
            assert device_tracker.load_config('nodict.yaml', **args) == []
            assert device_tracker.load_config('noname.yaml', **args) == []
            assert device_tracker.load_config('badkey.yaml', **args) == []

            res = device_tracker.load_config('allok.yaml', **args)
            assert len(res) == 1
            assert res[0].name == 'Device'
            assert res[0].dev_id == 'my_device'

            res = device_tracker.load_config('oneok.yaml', **args)
            assert len(res) == 1
            assert res[0].name == 'Device'
            assert res[0].dev_id == 'my_device'
示例#13
0
    def test_not_write_duplicate_yaml_keys(self):

    # pylint: disable=invalid-name
        """Test that the device tracker will not generate invalid YAML."""
        self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, TEST_PLATFORM))

        device_tracker.see(self.hass, "mac_1", host_name="hello")
        device_tracker.see(self.hass, "mac_2", host_name="hello")

        self.hass.block_till_done()

        config = device_tracker.load_config(self.yaml_devices, self.hass, timedelta(seconds=0))
        assert len(config) == 2
示例#14
0
    def test_not_write_duplicate_yaml_keys(self):
        """Test that the device tracker will not generate invalid YAML."""
        with assert_setup_component(1, device_tracker.DOMAIN):
            assert setup_component(self.hass, device_tracker.DOMAIN,
                                   TEST_PLATFORM)

        device_tracker.see(self.hass, 'mac_1', host_name='hello')
        device_tracker.see(self.hass, 'mac_2', host_name='hello')

        self.hass.block_till_done()

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert len(config) == 2
示例#15
0
    def test_adding_unknown_device_to_config(self): \
            # pylint: disable=invalid-name
        """Test the adding of unknown devices to configuration file."""
        scanner = get_component('device_tracker.test').SCANNER
        scanner.reset()
        scanner.come_home('DEV1')

        self.assertTrue(device_tracker.setup(self.hass, {
            device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}}))
        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert len(config) == 1
        assert config[0].dev_id == 'dev1'
        assert config[0].track
示例#16
0
 def test_reading_yaml_config(self):
     dev_id = 'test'
     device = device_tracker.Device(
         self.hass, timedelta(seconds=180), True, dev_id, 'AB:CD:EF:GH:IJ',
         'Test name', 'http://test.picture', True)
     device_tracker.update_config(self.yaml_devices, dev_id, device)
     self.assertTrue(device_tracker.setup(self.hass, {}))
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         device.consider_home)[0]
     self.assertEqual(device.dev_id, config.dev_id)
     self.assertEqual(device.track, config.track)
     self.assertEqual(device.mac, config.mac)
     self.assertEqual(device.config_picture, config.config_picture)
     self.assertEqual(device.away_hide, config.away_hide)
     self.assertEqual(device.consider_home, config.consider_home)
示例#17
0
 def test_reading_yaml_config(self):
     """Test the rendering of the YAML configuration."""
     dev_id = 'test'
     device = device_tracker.Device(
         self.hass, timedelta(seconds=180), True, dev_id,
         'AB:CD:EF:GH:IJ', 'Test name', picture='http://test.picture',
         away_hide=True)
     device_tracker.update_config(self.yaml_devices, dev_id, device)
     self.assertTrue(device_tracker.setup(self.hass, TEST_PLATFORM))
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         device.consider_home)[0]
     self.assertEqual(device.dev_id, config.dev_id)
     self.assertEqual(device.track, config.track)
     self.assertEqual(device.mac, config.mac)
     self.assertEqual(device.config_picture, config.config_picture)
     self.assertEqual(device.away_hide, config.away_hide)
     self.assertEqual(device.consider_home, config.consider_home)
示例#18
0
    def test_adding_unknown_device_to_config(self):
        """Test the adding of unknown devices to configuration file."""
        scanner = get_component('device_tracker.test').SCANNER
        scanner.reset()
        scanner.come_home('DEV1')

        with assert_setup_component(1, device_tracker.DOMAIN):
            assert setup_component(self.hass, device_tracker.DOMAIN, {
                device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}})

        # wait for async calls (macvendor) to finish
        self.hass.block_till_done()

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert len(config) == 1
        assert config[0].dev_id == 'dev1'
        assert config[0].track
示例#19
0
 def test_reading_yaml_config(self):
     """Test the rendering of the YAML configuration."""
     dev_id = 'test'
     device = device_tracker.Device(
         self.hass, timedelta(seconds=180), True, dev_id,
         'AB:CD:EF:GH:IJ', 'Test name', picture='http://test.picture',
         hide_if_away=True, icon='mdi:kettle')
     device_tracker.update_config(self.yaml_devices, dev_id, device)
     with assert_setup_component(1, device_tracker.DOMAIN):
         assert setup_component(self.hass, device_tracker.DOMAIN,
                                TEST_PLATFORM)
     config = device_tracker.load_config(self.yaml_devices, self.hass,
                                         device.consider_home)[0]
     assert device.dev_id == config.dev_id
     assert device.track == config.track
     assert device.mac == config.mac
     assert device.config_picture == config.config_picture
     assert device.away_hide == config.away_hide
     assert device.consider_home == config.consider_home
     assert device.icon == config.icon
示例#20
0
    def test_see_failures(self, mock_warning):
        """Test that the device tracker see failures."""
        tracker = device_tracker.DeviceTracker(self.hass, timedelta(seconds=60), 0, [])

        # MAC is not a string (but added)
        tracker.see(mac=567, host_name="Number MAC")

        # No device id or MAC(not added)
        with self.assertRaises(HomeAssistantError):
            tracker.see()
        assert mock_warning.call_count == 0

        # Ignore gps on invalid GPS (both added & warnings)
        tracker.see(mac="mac_1_bad_gps", gps=1)
        tracker.see(mac="mac_2_bad_gps", gps=[1])
        tracker.see(mac="mac_3_bad_gps", gps="gps")
        config = device_tracker.load_config(self.yaml_devices, self.hass, timedelta(seconds=0))
        assert mock_warning.call_count == 3

        assert len(config) == 4
示例#21
0
    def test_see_state(self):
        """Test device tracker see records state correctly."""
        assert setup_component(self.hass, device_tracker.DOMAIN,
                               TEST_PLATFORM)

        params = {
            'mac': 'AA:BB:CC:DD:EE:FF',
            'dev_id': 'some_device',
            'host_name': 'example.com',
            'location_name': 'Work',
            'gps': [.3, .8],
            'gps_accuracy': 1,
            'battery': 100,
            'attributes': {
                'test': 'test',
                'number': 1,
            },
        }

        device_tracker.see(self.hass, **params)
        self.hass.block_till_done()

        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert len(config) == 1

        state = self.hass.states.get('device_tracker.examplecom')
        attrs = state.attributes
        assert state.state == 'Work'
        assert state.object_id == 'examplecom'
        assert state.name == 'example.com'
        assert attrs['friendly_name'] == 'example.com'
        assert attrs['battery'] == 100
        assert attrs['latitude'] == 0.3
        assert attrs['longitude'] == 0.8
        assert attrs['test'] == 'test'
        assert attrs['gps_accuracy'] == 1
        assert attrs['source_type'] == 'gps'
        assert attrs['number'] == 1
示例#22
0
 def test_reading_yaml_config(self):
     """Test the rendering of the YAML configuration."""
     dev_id = "test"
     device = device_tracker.Device(
         self.hass,
         timedelta(seconds=180),
         True,
         dev_id,
         "AB:CD:EF:GH:IJ",
         "Test name",
         picture="http://test.picture",
         hide_if_away=True,
     )
     device_tracker.update_config(self.yaml_devices, dev_id, device)
     self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, TEST_PLATFORM))
     config = device_tracker.load_config(self.yaml_devices, self.hass, device.consider_home)[0]
     self.assertEqual(device.dev_id, config.dev_id)
     self.assertEqual(device.track, config.track)
     self.assertEqual(device.mac, config.mac)
     self.assertEqual(device.config_picture, config.config_picture)
     self.assertEqual(device.away_hide, config.away_hide)
     self.assertEqual(device.consider_home, config.consider_home)
示例#23
0
    def test_see_failures(self, mock_warning):
        """Test that the device tracker see failures."""
        tracker = device_tracker.DeviceTracker(
            self.hass, timedelta(seconds=60), 0, {}, [])

        # MAC is not a string (but added)
        tracker.see(mac=567, host_name="Number MAC")

        # No device id or MAC(not added)
        with pytest.raises(HomeAssistantError):
            run_coroutine_threadsafe(
                tracker.async_see(), self.hass.loop).result()
        assert mock_warning.call_count == 0

        # Ignore gps on invalid GPS (both added & warnings)
        tracker.see(mac='mac_1_bad_gps', gps=1)
        tracker.see(mac='mac_2_bad_gps', gps=[1])
        tracker.see(mac='mac_3_bad_gps', gps='gps')
        self.hass.block_till_done()
        config = device_tracker.load_config(self.yaml_devices, self.hass,
                                            timedelta(seconds=0))
        assert mock_warning.call_count == 3

        assert len(config) == 4
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth
    from bt_proximity import BluetoothRSSI

    def see_device(mac, name, rssi=None):
        """Mark a device as seen."""
        attributes = {}
        if rssi is not None:
            attributes['rssi'] = rssi
        see(mac="{}_{}".format(BT_PREFIX, mac), host_name=name,
            attributes=attributes, source_type=SOURCE_TYPE_BLUETOOTH)

    def discover_devices():
        """Discover Bluetooth devices."""
        result = bluetooth.discover_devices(
            duration=8, lookup_names=True, flush_cache=True,
            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = %d", len(result))
        return result

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # Check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # If track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
                    dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev[0], dev[1])

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    request_rssi = config.get(CONF_REQUEST_RSSI, False)

    def update_bluetooth(now):
        """Lookup Bluetooth device and update status."""
        try:
            if track_new:
                for dev in discover_devices():
                    if dev[0] not in devs_to_track and \
                            dev[0] not in devs_donot_track:
                        devs_to_track.append(dev[0])
            for mac in devs_to_track:
                _LOGGER.debug("Scanning %s", mac)
                result = bluetooth.lookup_name(mac, timeout=5)
                rssi = None
                if request_rssi:
                    rssi = BluetoothRSSI(mac).request_rssi()
                if result is None:
                    # Could not lookup device name
                    continue
                see_device(mac, result, rssi)
        except bluetooth.BluetoothError:
            _LOGGER.exception("Error looking up Bluetooth device")
        track_point_in_utc_time(
            hass, update_bluetooth, dt_util.utcnow() + interval)

    update_bluetooth(dt_util.utcnow())

    return True
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth
    import bluetooth._bluetooth as bluez

    hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_ON)

    def turn_on(call):
        """Turn Bluetooth tracker on."""
        _LOGGER.info("Turning on Bluetooth")
        hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_ON)

    def turn_off(call):
        """Turn Bluetooth tracker off."""
        _LOGGER.info("Turning off Bluetooth")

        try:
            sock = bluez.hci_open_dev(0)
            bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL,
                               bluez.OCF_INQUIRY_CANCEL)
            sock.close()

            _LOGGER.info("Turned off Bluetooth")
            hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_OFF)

        except Exception as err:
            _LOGGER.error("Error turning off Bluetooth: %s", err)
            sock.close()

    def see_device(device):
        """Mark a device as seen."""
        see(mac=BT_PREFIX + device[0], host_name=device[1])

    def discover_devices():
        if hass.states.get(DOMAIN + '.' + ENTITY_ID).state != STATE_ON:
            return []

        _LOGGER.debug("Discovering Bluetooth devices")
        """Discover Bluetooth devices."""
        result = bluetooth.discover_devices(duration=8,
                                            lookup_names=True,
                                            flush_cache=True,
                                            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
        return result

    hass.services.register(DOMAIN,
                           BLUETOOTH_TRACKER_SERVICE_TURN_ON,
                           turn_on,
                           schema=BLUETOOTH_TRACKER_SERVICE_SCHEMA)

    hass.services.register(DOMAIN,
                           BLUETOOTH_TRACKER_SERVICE_TURN_OFF,
                           turn_off,
                           schema=BLUETOOTH_TRACKER_SERVICE_SCHEMA)

    # Ensure the Bluetooth tracker is on (if that state has been set)
    if hass.states.get(DOMAIN + '.' + ENTITY_ID).state == STATE_ON:
        turn_on(None)

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # Check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # If track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_bluetooth(now):
        """Lookup Bluetooth device and update status."""
        try:
            if track_new:
                for dev in discover_devices():
                    if dev[0] not in devs_to_track and \
                       dev[0] not in devs_donot_track:
                        devs_to_track.append(dev[0])
            for mac in devs_to_track:
                if hass.states.get(DOMAIN + '.' + ENTITY_ID).state != STATE_ON:
                    continue

                _LOGGER.debug("Scanning %s", mac)

                result = bluetooth.lookup_name(mac, timeout=5)
                if not result:
                    # Could not lookup device name
                    continue
                see_device((mac, result))
        except bluetooth.BluetoothError:
            _LOGGER.exception("Error looking up Bluetooth device")
        track_point_in_utc_time(hass, update_bluetooth,
                                dt_util.utcnow() + interval)

    update_bluetooth(dt_util.utcnow())

    return True
def setup_scanner(hass, config, see, discovery_info=None):
    """Setup the Bluetooth LE Scanner."""
    # pylint: disable=import-error
    from gattlib import DiscoveryService

    new_devices = {}

    def see_device(address, name, new_device=False):
        """Mark a device as seen."""
        if new_device:
            if address in new_devices:
                _LOGGER.debug("Seen %s %s times", address,
                              new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        see(mac=BLE_PREFIX + address, host_name=name.strip("\x00"))

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            service = DiscoveryService(ble_dev_id)
            devices = service.discover(duration)
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except RuntimeError as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            devices = []
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    duration = config.get(CONF_SCAN_DURATION)
    ble_dev_id = config.get(CONF_BLUETOOTH_DEVICE)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        for mac in devs_to_track:
            _LOGGER.debug("Checking " + mac)
            result = mac in devs
            if not result:
                # Could not lookup device name
                continue
            see_device(mac, devs[mac])

        if track_new:
            for address in devs:
                if address not in devs_to_track and \
                        address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, now + interval)

    update_ble(dt_util.utcnow())

    return True
示例#27
0
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth LE Scanner."""
    # pylint: disable=import-error
    import pygatt
    new_devices = {}
    hass.data.setdefault(DATA_BLE, {DATA_BLE_ADAPTER: None})

    async def async_stop(event):
        """Try to shut down the bluetooth child process nicely."""
        # These should never be unset at the point this runs, but just for
        # safety's sake, use `get`.
        adapter = hass.data.get(DATA_BLE, {}).get(DATA_BLE_ADAPTER)
        if adapter is not None:
            adapter.kill()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop)

    def see_device(address, name, new_device=False):
        """Mark a device as seen."""
        if new_device:
            if address in new_devices:
                _LOGGER.debug(
                    "Seen %s %s times", address, new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        if name is not None:
            name = name.strip("\x00")

        see(mac=BLE_PREFIX + address, host_name=name,
            source_type=SOURCE_TYPE_BLUETOOTH_LE)

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            adapter = pygatt.GATTToolBackend()
            hass.data[DATA_BLE][DATA_BLE_ADAPTER] = adapter
            devs = adapter.scan()

            devices = {x['address']: x['name'] for x in devs}
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except RuntimeError as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            return {}
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        for mac in devs_to_track:
            if mac not in devs:
                continue

            if devs[mac] is None:
                devs[mac] = mac
            see_device(mac, devs[mac])

        if track_new:
            for address in devs:
                if address not in devs_to_track and \
                        address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, dt_util.utcnow() + interval)

    update_ble(dt_util.utcnow())
    return True
def setup_scanner(hass, config, see, discovery_info=None):
    """Setup the Sleep Cycle Scanner."""

    # pylint: disable=import-error

    def see_device(device):
        """Mark a device as seen."""
        see(mac=SS_PREFIX + device[0], host_name=device[1])

    def discover_devices():
        """Discover bluetooth devices."""
        result = SleepCycleHosts.find()
        _LOGGER.debug("Sleep Cycle discovered = " + str(len(result)))
        return list(result)

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == SS_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # if track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_sleepcycle(now):
        """Look for Sleep Cycle hosts and update status."""
        if track_new:
            for dev in discover_devices():
                if dev[0] not in devs_to_track and \
                   dev[0] not in devs_donot_track:
                    devs_to_track.append(dev[0])
        for mac in devs_to_track:
            _LOGGER.debug("Scanning " + mac)
            result = bluetooth.lookup_name(mac, timeout=5)
            if not result:
                # Could not lookup device name
                continue
            see_device((mac, result))

        track_point_in_utc_time(hass, update_bluetooth,
                                dt_util.utcnow() + interval)

    update_sleepcycle(dt_util.utcnow())

    return True
示例#29
0
def setup_scanner(hass, config, see):
    """Setup the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth

    def see_device(device):
        """Mark a device as seen."""
        see(mac=BT_PREFIX + device[0], host_name=device[1])

    def discover_devices():
        """Discover bluetooth devices."""
        result = bluetooth.discover_devices(duration=8,
                                            lookup_names=True,
                                            flush_cache=True,
                                            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
        return result

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # if track new devices is true discover new devices
    # on startup.
    track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
                             len(devs_to_track) == 0)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    if not devs_to_track:
        _LOGGER.warning("No bluetooth devices to track!")
        return False

    interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
                            DEFAULT_SCAN_INTERVAL)

    def update_bluetooth(now):
        """Lookup bluetooth device and update status."""
        try:
            for mac in devs_to_track:
                _LOGGER.debug("Scanning " + mac)
                result = bluetooth.lookup_name(mac, timeout=5)
                if not result:
                    # Could not lookup device name
                    continue
                see_device((mac, result))
        except bluetooth.BluetoothError:
            _LOGGER.exception('Error looking up bluetooth device!')
        track_point_in_utc_time(hass, update_bluetooth,
                                now + timedelta(seconds=interval))

    update_bluetooth(dt_util.utcnow())

    return True
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth
    import bluetooth._bluetooth as bluez

    hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_ON)

    def turn_on(call):
        """Turn Bluetooth tracker on."""
        _LOGGER.info("Turning on Bluetooth")
        hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_ON)

    def turn_off(call):
        """Turn Bluetooth tracker off."""
        _LOGGER.info("Turning off Bluetooth")

        try:
            sock = bluez.hci_open_dev(0)
            bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL, bluez.OCF_INQUIRY_CANCEL)
            sock.close()

            _LOGGER.info("Turned off Bluetooth")
            hass.states.set(DOMAIN + '.' + ENTITY_ID, STATE_OFF)

        except Exception as err:
            _LOGGER.error("Error turning off Bluetooth: %s", err)
            sock.close()

    def see_device(device):
        """Mark a device as seen."""
        see(mac=BT_PREFIX + device[0], host_name=device[1])

    def discover_devices():
        if hass.states.get(DOMAIN + '.' + ENTITY_ID).state != STATE_ON:
            return []

        _LOGGER.debug("Discovering Bluetooth devices")

        """Discover Bluetooth devices."""
        result = bluetooth.discover_devices(
            duration=8, lookup_names=True, flush_cache=True,
            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
        return result

    hass.services.register(
        DOMAIN, BLUETOOTH_TRACKER_SERVICE_TURN_ON, turn_on, schema=BLUETOOTH_TRACKER_SERVICE_SCHEMA)

    hass.services.register(
        DOMAIN, BLUETOOTH_TRACKER_SERVICE_TURN_OFF, turn_off, schema=BLUETOOTH_TRACKER_SERVICE_SCHEMA)

    # Ensure the Bluetooth tracker is on (if that state has been set)
    if hass.states.get(DOMAIN + '.' + ENTITY_ID).state == STATE_ON:
        turn_on(None)

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # Check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # If track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_bluetooth(now):
        """Lookup Bluetooth device and update status."""
        try:
            if track_new:
                for dev in discover_devices():
                    if dev[0] not in devs_to_track and \
                       dev[0] not in devs_donot_track:
                        devs_to_track.append(dev[0])
            for mac in devs_to_track:
                if hass.states.get(DOMAIN + '.' + ENTITY_ID).state != STATE_ON:
                    continue

                _LOGGER.debug("Scanning %s", mac)

                result = bluetooth.lookup_name(mac, timeout=5)
                if not result:
                    # Could not lookup device name
                    continue
                see_device((mac, result))
        except bluetooth.BluetoothError:
            _LOGGER.exception("Error looking up Bluetooth device")
        track_point_in_utc_time(
            hass, update_bluetooth, dt_util.utcnow() + interval)

    update_bluetooth(dt_util.utcnow())

    return True
示例#31
0
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth LE Scanner."""
    # pylint: disable=import-error
    import pygatt
    from pygatt.exceptions import BLEError
    new_devices = {}

    def see_device(address, name, new_device=False):
        """Mark a device as seen."""
        if new_device:
            if address in new_devices:
                _LOGGER.debug(
                    "Seen %s %s times", address, new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        if name is not None:
            name = name.strip("\x00")

        see(mac=BLE_PREFIX + address, host_name=name,
            source_type=SOURCE_TYPE_BLUETOOTH_LE)

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            adapter = pygatt.GATTToolBackend()
            devs = adapter.scan()

            devices = {x['address']: x['name'] for x in devs}
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except BLEError as error:
            _LOGGER.error("Caught pygatt BLE Error: %s - will try again later", error)
            return {}
        except RuntimeError as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            return {}
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        for mac in devs_to_track:
            if mac not in devs:
                continue

            if devs[mac] is None:
                devs[mac] = mac
            see_device(mac, devs[mac])

        if track_new:
            for address in devs:
                if address not in devs_to_track and \
                        address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, dt_util.utcnow() + interval)

    update_ble(dt_util.utcnow())
    return True
示例#32
0
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth
    from bt_proximity import BluetoothRSSI

    def see_device(mac, name, rssi=None):
        """Mark a device as seen."""
        attributes = {}
        if rssi is not None:
            attributes['rssi'] = rssi
        see(mac="{}{}".format(BT_PREFIX, mac),
            host_name=name,
            attributes=attributes,
            source_type=SOURCE_TYPE_BLUETOOTH)

    device_id = config.get(CONF_DEVICE_ID)

    def discover_devices():
        """Discover Bluetooth devices."""
        result = bluetooth.discover_devices(duration=8,
                                            lookup_names=True,
                                            flush_cache=True,
                                            lookup_class=False,
                                            device_id=device_id)
        _LOGGER.debug("Bluetooth devices discovered = %d", len(result))
        return result

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # Check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # If track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
                    dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev[0], dev[1])

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    request_rssi = config.get(CONF_REQUEST_RSSI, False)

    def update_bluetooth(_):
        """Update Bluetooth and set timer for the next update."""
        update_bluetooth_once()
        track_point_in_utc_time(hass, update_bluetooth,
                                dt_util.utcnow() + interval)

    def update_bluetooth_once():
        """Lookup Bluetooth device and update status."""
        try:
            if track_new:
                for dev in discover_devices():
                    if dev[0] not in devs_to_track and \
                            dev[0] not in devs_donot_track:
                        devs_to_track.append(dev[0])
            for mac in devs_to_track:
                _LOGGER.debug("Scanning %s", mac)
                result = bluetooth.lookup_name(mac, timeout=5)
                rssi = None
                if request_rssi:
                    rssi = BluetoothRSSI(mac).request_rssi()
                if result is None:
                    # Could not lookup device name
                    continue
                see_device(mac, result, rssi)
        except bluetooth.BluetoothError:
            _LOGGER.exception("Error looking up Bluetooth device")

    def handle_update_bluetooth(call):
        """Update bluetooth devices on demand."""
        update_bluetooth_once()

    update_bluetooth(dt_util.utcnow())

    hass.services.register(DOMAIN, "bluetooth_tracker_update",
                           handle_update_bluetooth)

    return True
def setup_scanner(hass, config, see):
    """Setup the Bluetooth LE Scanner."""
    # pylint: disable=import-error
    from gattlib import DiscoveryService

    new_devices = {}

    def see_device(address, name, new_device=False):
        """Mark a device as seen."""
        if new_device:
            if address in new_devices:
                _LOGGER.debug("Seen %s %s times", address,
                              new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        see(mac=BLE_PREFIX + address, host_name=name.strip("\x00"))

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        service = DiscoveryService()
        devices = service.discover(duration)
        _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)

        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    duration = config.get(CONF_SCAN_DURATION)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
                             len(devs_to_track) == 0)
    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
                            DEFAULT_SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        for mac in devs_to_track:
            _LOGGER.debug("Checking " + mac)
            result = mac in devs
            if not result:
                # Could not lookup device name
                continue
            see_device(mac, devs[mac])

        if track_new:
            for address in devs:
                if address not in devs_to_track and \
                        address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble,
                                now + timedelta(seconds=interval))

    update_ble(dt_util.utcnow())

    return True
示例#34
0
def setup_scanner(hass, config, see):
    """Setup the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth

    def see_device(device):
        """Mark a device as seen."""
        see(mac=BT_PREFIX + device[0], host_name=device[1])

    def discover_devices():
        """Discover bluetooth devices."""
        result = bluetooth.discover_devices(duration=8,
                                            lookup_names=True,
                                            flush_cache=True,
                                            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
        return result

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # if track new devices is true discover new devices
    # on startup.
    track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
                             len(devs_to_track) == 0)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    if not devs_to_track:
        _LOGGER.warning("No bluetooth devices to track!")
        return False

    interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
                            DEFAULT_SCAN_INTERVAL)

    def update_bluetooth(now):
        """Lookup bluetooth device and update status."""
        try:
            for mac in devs_to_track:
                _LOGGER.debug("Scanning " + mac)
                result = bluetooth.lookup_name(mac, timeout=5)
                if not result:
                    # Could not lookup device name
                    continue
                see_device((mac, result))
        except bluetooth.BluetoothError:
            _LOGGER.exception('Error looking up bluetooth device!')
        track_point_in_utc_time(hass, update_bluetooth,
                                now + timedelta(seconds=interval))

    update_bluetooth(dt_util.utcnow())

    return True
示例#35
0
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth Scanner."""
    # pylint: disable=import-error
    import bluetooth

    def see_device(device):
        """Mark a device as seen."""
        see(mac=BT_PREFIX + device[0],
            host_name=device[1],
            source_type=SOURCE_TYPE_BLUETOOTH)

    def discover_devices():
        """Discover Bluetooth devices."""
        result = bluetooth.discover_devices(duration=8,
                                            lookup_names=True,
                                            flush_cache=True,
                                            lookup_class=False)
        _LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
        return result

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # Check if device is a valid bluetooth device
        if device.mac and device.mac[:3].upper() == BT_PREFIX:
            if device.track:
                devs_to_track.append(device.mac[3:])
            else:
                devs_donot_track.append(device.mac[3:])

    # If track new devices is true discover new devices on startup.
    track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
    if track_new:
        for dev in discover_devices():
            if dev[0] not in devs_to_track and \
               dev[0] not in devs_donot_track:
                devs_to_track.append(dev[0])
                see_device(dev)

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_bluetooth(now):
        """Lookup Bluetooth device and update status."""
        try:
            if track_new:
                for dev in discover_devices():
                    if dev[0] not in devs_to_track and \
                       dev[0] not in devs_donot_track:
                        devs_to_track.append(dev[0])
            for mac in devs_to_track:
                _LOGGER.debug("Scanning %s", mac)
                result = bluetooth.lookup_name(mac, timeout=5)
                if not result:
                    # Could not lookup device name
                    continue
                see_device((mac, result))
        except bluetooth.BluetoothError:
            _LOGGER.exception("Error looking up Bluetooth device")
        track_point_in_utc_time(hass, update_bluetooth,
                                dt_util.utcnow() + interval)

    update_bluetooth(dt_util.utcnow())

    return True
def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth LE Scanner."""
    # pylint: disable=import-error
    import pygatt
    new_devices = {}

    def see_device(address, name, new_device=False):
        """Mark a device as seen."""
        if new_device:
            if address in new_devices:
                _LOGGER.debug(
                    "Seen %s %s times", address, new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        if name is not None:
            name = name.strip("\x00")

        see(mac=BLE_PREFIX + address, host_name=name,
            source_type=SOURCE_TYPE_BLUETOOTH_LE)

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            adapter = pygatt.GATTToolBackend()
            devs = adapter.scan()

            devices = {x['address']: x['name'] for x in devs}
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except RuntimeError as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            return {}
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        for mac in devs_to_track:
            if mac not in devs:
                continue

            if devs[mac] is None:
                devs[mac] = mac
            see_device(mac, devs[mac])

        if track_new:
            for address in devs:
                if address not in devs_to_track and \
                        address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, dt_util.utcnow() + interval)

    update_ble(dt_util.utcnow())
    return True
def setup_scanner(hass, config, see, discovery_info):
    """Setup the Bluetooth LE Scanner."""
    import pygatt

    class Monitor(threading.Thread):
        """Connection handling."""
        def __init__(self, hass, see):
            """Construct interface object."""
            threading.Thread.__init__(self)
            self.daemon = False
            self.hass = hass
            self.keep_going = True
            self.event = threading.Event()

        def mycallback(self, address):
            print("device %s" % address)
            see_device(address)

        def run(self):
            """Thread that keeps connection alive."""
            import pygatt

            adapter = pygatt.backends.GATTToolBackend()
            adapter.passive_scan(1, callback=self.mycallback)

        def terminate(self):
            """Signal runner to stop and join thread."""
            self.keep_going = False
            self.event.set()
            self.join()

    def see_device(address):
        """Mark a device as seen."""

        if address in devs_donot_track:
            return

        if track_new and address not in devs_to_track and address not in devs_donot_track:
            if address in new_devices:
                _LOGGER.debug("Seen %s %s times", address,
                              new_devices[address])
                new_devices[address] += 1
                if new_devices[address] >= MIN_SEEN_NEW:
                    _LOGGER.debug("Adding %s to tracked devices", address)
                    devs_to_track.append(address)
                else:
                    return
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = 1
                return

        if address in devs_to_track:
            see(mac=BLE_PREFIX + address)

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            service = DiscoveryService(ble_dev_id)
            devices = service.discover(duration)
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except RuntimeError as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            devices = []
        return devices

    def monitor_stop(_service_or_event):
        """Stop the monitor thread."""
        _LOGGER.info("Stopping monitor for")
        mon.terminate()

    new_devices = {}
    yaml_path = hass.config.path(YAML_DEVICES)
    ble_dev_id = config.get(CONF_BLUETOOTH_DEVICE)
    devs_to_track = []
    devs_donot_track = []

    # Load all known devices.
    for device in load_config(yaml_path, hass, 0):
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(device.mac[4:])
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(device.mac[4:])

    # if track new devices is true discover new devices
    # on every scan.
    track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
                             DEFAULT_TRACK_NEW)
    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    mon = Monitor(hass, see)
    mon.start()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, monitor_stop)

    return True