def setup(hass, config): """Setup input select.""" if not isinstance(config.get(DOMAIN), dict): _LOGGER.error('Expected %s config to be a dictionary', DOMAIN) return False component = EntityComponent(_LOGGER, DOMAIN, hass) entities = [] for object_id, cfg in config[DOMAIN].items(): if object_id != slugify(object_id): _LOGGER.warning( "Found invalid key for boolean input: %s. " "Use %s instead", object_id, slugify(object_id)) continue if not cfg: _LOGGER.warning("No configuration specified for %s", object_id) continue name = cfg.get(CONF_NAME) options = cfg.get(CONF_OPTIONS) if not isinstance(options, list) or len(options) == 0: _LOGGER.warning('Key %s should be a list of options', CONF_OPTIONS) continue options = [str(val) for val in options] state = cfg.get(CONF_INITIAL) if state not in options: state = options[0] icon = cfg.get(CONF_ICON) entities.append(InputSelect(object_id, name, state, options, icon)) if not entities: return False def select_option_service(call): """Handle a calls to the input select services.""" target_inputs = component.extract_from_service(call) for input_select in target_inputs: input_select.select_option(call.data[ATTR_OPTION]) hass.services.register(DOMAIN, SERVICE_SELECT_OPTION, select_option_service, schema=SERVICE_SELECT_OPTION_SCHEMA) component.add_entities(entities) return True
def setup(hass, config): """Set up input slider.""" if not isinstance(config.get(DOMAIN), dict): _LOGGER.error('Expected %s config to be a dictionary', DOMAIN) return False component = EntityComponent(_LOGGER, DOMAIN, hass) entities = [] for object_id, cfg in config[DOMAIN].items(): if object_id != slugify(object_id): _LOGGER.warning( "Found invalid key for boolean input: %s. " "Use %s instead", object_id, slugify(object_id)) continue if not cfg: _LOGGER.warning("No configuration specified for %s", object_id) continue name = cfg.get(CONF_NAME) minimum = cfg.get(CONF_MIN) maximum = cfg.get(CONF_MAX) state = cfg.get(CONF_INITIAL, minimum) step = cfg.get(CONF_STEP, 1) icon = cfg.get(CONF_ICON) unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT) if state < minimum: state = minimum if state > maximum: state = maximum entities.append( InputSlider(object_id, name, state, minimum, maximum, step, icon, unit)) if not entities: return False def select_value_service(call): """Handle a calls to the input slider services.""" target_inputs = component.extract_from_service(call) for input_slider in target_inputs: input_slider.select_value(call.data[ATTR_VALUE]) hass.services.register(DOMAIN, SERVICE_SELECT_VALUE, select_value_service, schema=SERVICE_SELECT_VALUE_SCHEMA) component.add_entities(entities) return True
def setup(hass, config): """Set up input boolean.""" if not isinstance(config.get(DOMAIN), dict): _LOGGER.error('Expected %s config to be a dictionary', DOMAIN) return False component = EntityComponent(_LOGGER, DOMAIN, hass) entities = [] for object_id, cfg in config[DOMAIN].items(): if object_id != slugify(object_id): _LOGGER.warning( "Found invalid key for boolean input: %s. " "Use %s instead", object_id, slugify(object_id)) continue if not cfg: cfg = {} name = cfg.get(CONF_NAME) state = cfg.get(CONF_INITIAL, False) icon = cfg.get(CONF_ICON) entities.append(InputBoolean(object_id, name, state, icon)) if not entities: return False def toggle_service(service): """Handle a calls to the input boolean services.""" target_inputs = component.extract_from_service(service) for input_b in target_inputs: if service.service == SERVICE_TURN_ON: input_b.turn_on() else: input_b.turn_off() hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service, schema=TOGGLE_SERVICE_SCHEMA) hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service, schema=TOGGLE_SERVICE_SCHEMA) component.add_entities(entities) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup template binary sensors.""" sensors = [] if config.get(CONF_SENSORS) is None: _LOGGER.error('Missing configuration data for binary_sensor platform') return False for device, device_config in config[CONF_SENSORS].items(): if device != slugify(device): _LOGGER.error('Found invalid key for binary_sensor.template: %s. ' 'Use %s instead', device, slugify(device)) continue if not isinstance(device_config, dict): _LOGGER.error('Missing configuration data for binary_sensor %s', device) continue friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) sensor_class = device_config.get('sensor_class') value_template = device_config.get(CONF_VALUE_TEMPLATE) if sensor_class not in SENSOR_CLASSES: _LOGGER.error('Sensor class is not valid') continue if value_template is None: _LOGGER.error( 'Missing %s for sensor %s', CONF_VALUE_TEMPLATE, device) continue entity_ids = device_config.get(ATTR_ENTITY_ID, MATCH_ALL) sensors.append( BinarySensorTemplate( hass, device, friendly_name, sensor_class, value_template, entity_ids) ) if not sensors: _LOGGER.error('No sensors added') return False add_devices(sensors) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Template switch.""" switches = [] if config.get(CONF_SWITCHES) is None: _LOGGER.error("Missing configuration data for switch platform") return False for device, device_config in config[CONF_SWITCHES].items(): if device != slugify(device): _LOGGER.error("Found invalid key for switch.template: %s. " "Use %s instead", device, slugify(device)) continue if not isinstance(device_config, dict): _LOGGER.error("Missing configuration data for switch %s", device) continue friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) state_template = device_config.get(CONF_VALUE_TEMPLATE) on_action = device_config.get(ON_ACTION) off_action = device_config.get(OFF_ACTION) if state_template is None: _LOGGER.error( "Missing %s for switch %s", CONF_VALUE_TEMPLATE, device) continue if on_action is None or off_action is None: _LOGGER.error( "Missing action for switch %s", device) continue entity_ids = device_config.get(ATTR_ENTITY_ID, MATCH_ALL) switches.append( SwitchTemplate( hass, device, friendly_name, state_template, on_action, off_action, entity_ids) ) if not switches: _LOGGER.error("No switches added") return False add_devices(switches) return True
def apply_received_command(event): """Apply command from rfxtrx.""" device_id = slugify(event.device.id_string.lower()) # Check if entity exists or previously added automatically if device_id not in RFX_DEVICES: return _LOGGER.debug("EntityID: %s device_update. Command: %s", device_id, event.values['Command']) if event.values['Command'] == 'On'\ or event.values['Command'] == 'Off': # Update the rfxtrx device state is_on = event.values['Command'] == 'On' RFX_DEVICES[device_id].update_state(is_on) elif hasattr(RFX_DEVICES[device_id], 'brightness')\ and event.values['Command'] == 'Set level': _brightness = (event.values['Dim level'] * 255 // 100) # Update the rfxtrx device state is_on = _brightness > 0 RFX_DEVICES[device_id].update_state(is_on, _brightness) # Fire event if RFX_DEVICES[device_id].should_fire_event: RFX_DEVICES[device_id].hass.bus.fire( EVENT_BUTTON_PRESSED, { ATTR_ENTITY_ID: RFX_DEVICES[device_id].entity_id, ATTR_STATE: event.values['Command'].lower() })
def sensor_update(event): """Callback for sensor updates from the RFXtrx gateway.""" if not isinstance(event, SensorEvent): return device_id = "sensor_" + slugify(event.device.id_string.lower()) if device_id in rfxtrx.RFX_DEVICES: sensors = rfxtrx.RFX_DEVICES[device_id] for key in sensors: sensors[key].event = event return # Add entity if not exist and the automatic_add is True if not config[ATTR_AUTOMATIC_ADD]: return pkt_id = "".join("{0:02x}".format(x) for x in event.data) _LOGGER.info("Automatic add rfxtrx.sensor: %s", device_id) data_type = "Unknown" for _data_type in DATA_TYPES: if _data_type in event.values: data_type = _data_type break new_sensor = RfxtrxSensor(event, pkt_id, data_type) sub_sensors = {} sub_sensors[new_sensor.data_type] = new_sensor rfxtrx.RFX_DEVICES[device_id] = sub_sensors add_devices_callback([new_sensor])
def see(self, mac=None, dev_id=None, host_name=None, location_name=None, gps=None, gps_accuracy=None, battery=None): """Notify the device tracker that you see a device.""" with self.lock: if mac is None and dev_id is None: raise BluMateError('Neither mac or device id passed in') elif mac is not None: mac = mac.upper() device = self.mac_to_dev.get(mac) if not device: dev_id = util.slugify(host_name or '') or util.slugify(mac) else: dev_id = cv.slug(str(dev_id).lower()) device = self.devices.get(dev_id) if device: device.seen(host_name, location_name, gps, gps_accuracy, battery) if device.track: device.update_ha_state() return # If no device can be found, create it dev_id = util.ensure_unique_string(dev_id, self.devices.keys()) device = Device(self.hass, self.consider_home, self.home_range, self.track_new, dev_id, mac, (host_name or dev_id).replace('_', ' ')) self.devices[dev_id] = device if mac is not None: self.mac_to_dev[mac] = device device.seen(host_name, location_name, gps, gps_accuracy, battery) if device.track: device.update_ha_state() # During init, we ignore the group if self.group is not None: self.group.update_tracked_entity_ids( list(self.group.tracking) + [device.entity_id]) update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
def __init__(self, hass, name, url, icon): """Initialize the link.""" self.hass = hass self._name = name self._url = url self._icon = icon self.entity_id = DOMAIN + '.%s' % slugify(name) self.update_ha_state()
def setup(hass, config): """Set up input boolean.""" if not isinstance(config.get(DOMAIN), dict): _LOGGER.error('Expected %s config to be a dictionary', DOMAIN) return False component = EntityComponent(_LOGGER, DOMAIN, hass) entities = [] for object_id, cfg in config[DOMAIN].items(): if object_id != slugify(object_id): _LOGGER.warning("Found invalid key for boolean input: %s. " "Use %s instead", object_id, slugify(object_id)) continue if not cfg: cfg = {} name = cfg.get(CONF_NAME) state = cfg.get(CONF_INITIAL, False) icon = cfg.get(CONF_ICON) entities.append(InputBoolean(object_id, name, state, icon)) if not entities: return False def toggle_service(service): """Handle a calls to the input boolean services.""" target_inputs = component.extract_from_service(service) for input_b in target_inputs: if service.service == SERVICE_TURN_ON: input_b.turn_on() else: input_b.turn_off() hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service, schema=TOGGLE_SERVICE_SCHEMA) hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service, schema=TOGGLE_SERVICE_SCHEMA) component.add_entities(entities) return True
def scene_activated(node, scene_id): """Called when a scene is activated on any node in the network.""" name = _node_name(node) object_id = "{}_{}".format(slugify(name), node.node_id) hass.bus.fire(EVENT_SCENE_ACTIVATED, { ATTR_ENTITY_ID: object_id, ATTR_SCENE_ID: scene_id })
def slug(value): """Validate value is a valid slug.""" if value is None: raise vol.Invalid('Slug should not be None') value = str(value) slg = slugify(value) if value == slg: return value raise vol.Invalid('invalid slug {} (try {})'.format(value, slg))
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup template binary sensors.""" sensors = [] if config.get(CONF_SENSORS) is None: _LOGGER.error('Missing configuration data for binary_sensor platform') return False for device, device_config in config[CONF_SENSORS].items(): if device != slugify(device): _LOGGER.error( 'Found invalid key for binary_sensor.template: %s. ' 'Use %s instead', device, slugify(device)) continue if not isinstance(device_config, dict): _LOGGER.error('Missing configuration data for binary_sensor %s', device) continue friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) sensor_class = device_config.get('sensor_class') value_template = device_config.get(CONF_VALUE_TEMPLATE) if sensor_class not in SENSOR_CLASSES: _LOGGER.error('Sensor class is not valid') continue if value_template is None: _LOGGER.error('Missing %s for sensor %s', CONF_VALUE_TEMPLATE, device) continue entity_ids = device_config.get(ATTR_ENTITY_ID, MATCH_ALL) sensors.append( BinarySensorTemplate(hass, device, friendly_name, sensor_class, value_template, entity_ids)) if not sensors: _LOGGER.error('No sensors added') return False add_devices(sensors) return True
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None): """Generate a unique entity ID based on given entity IDs or used IDs.""" name = (name or DEVICE_DEFAULT_NAME).lower() if current_ids is None: if hass is None: raise RuntimeError("Missing required parameter currentids or hass") current_ids = hass.states.entity_ids() return ensure_unique_string( entity_id_format.format(slugify(name)), current_ids)
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None): """Generate a unique entity ID based on given entity IDs or used IDs.""" name = (name or DEVICE_DEFAULT_NAME).lower() if current_ids is None: if hass is None: raise RuntimeError("Missing required parameter currentids or hass") current_ids = hass.states.entity_ids() return ensure_unique_string(entity_id_format.format(slugify(name)), current_ids)
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the template sensors.""" sensors = [] if config.get(CONF_SENSORS) is None: _LOGGER.error("Missing configuration data for sensor platform") return False for device, device_config in config[CONF_SENSORS].items(): if device != slugify(device): _LOGGER.error("Found invalid key for sensor.template: %s. " "Use %s instead", device, slugify(device)) continue if not isinstance(device_config, dict): _LOGGER.error("Missing configuration data for sensor %s", device) continue friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT) state_template = device_config.get(CONF_VALUE_TEMPLATE) if state_template is None: _LOGGER.error( "Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device) continue entity_ids = device_config.get(ATTR_ENTITY_ID, MATCH_ALL) sensors.append( SensorTemplate( hass, device, friendly_name, unit_of_measurement, state_template, entity_ids) ) if not sensors: _LOGGER.error("No sensors added") return False add_devices(sensors) return True
def see(self, mac=None, dev_id=None, host_name=None, location_name=None, gps=None, gps_accuracy=None, battery=None): """Notify the device tracker that you see a device.""" with self.lock: if mac is None and dev_id is None: raise BluMateError('Neither mac or device id passed in') elif mac is not None: mac = mac.upper() device = self.mac_to_dev.get(mac) if not device: dev_id = util.slugify(host_name or '') or util.slugify(mac) else: dev_id = cv.slug(str(dev_id).lower()) device = self.devices.get(dev_id) if device: device.seen(host_name, location_name, gps, gps_accuracy, battery) if device.track: device.update_ha_state() return # If no device can be found, create it dev_id = util.ensure_unique_string(dev_id, self.devices.keys()) device = Device( self.hass, self.consider_home, self.home_range, self.track_new, dev_id, mac, (host_name or dev_id).replace('_', ' ')) self.devices[dev_id] = device if mac is not None: self.mac_to_dev[mac] = device device.seen(host_name, location_name, gps, gps_accuracy, battery) if device.track: device.update_ha_state() # During init, we ignore the group if self.group is not None: self.group.update_tracked_entity_ids( list(self.group.tracking) + [device.entity_id]) update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
def _object_id(value): """Return the object_id of the device value. The object_id contains node_id and value instance id to not collide with other entity_ids. """ object_id = "{}_{}".format(slugify(_value_name(value)), value.node.node_id) # Add the instance id if there is more than one instance for the value if value.instance > 1: return "{}_{}".format(object_id, value.instance) return object_id
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the template sensors.""" sensors = [] if config.get(CONF_SENSORS) is None: _LOGGER.error("Missing configuration data for sensor platform") return False for device, device_config in config[CONF_SENSORS].items(): if device != slugify(device): _LOGGER.error( "Found invalid key for sensor.template: %s. " "Use %s instead", device, slugify(device)) continue if not isinstance(device_config, dict): _LOGGER.error("Missing configuration data for sensor %s", device) continue friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device) unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT) state_template = device_config.get(CONF_VALUE_TEMPLATE) if state_template is None: _LOGGER.error("Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device) continue entity_ids = device_config.get(ATTR_ENTITY_ID, MATCH_ALL) sensors.append( SensorTemplate(hass, device, friendly_name, unit_of_measurement, state_template, entity_ids)) if not sensors: _LOGGER.error("No sensors added") return False add_devices(sensors) return True
def _parse_see_args(topic, data): """Parse the OwnTracks location parameters, into the format see expects.""" parts = topic.split('/') dev_id = slugify('{}_{}'.format(parts[1], parts[2])) host_name = parts[1] kwargs = { 'dev_id': dev_id, 'host_name': host_name, 'gps': (data['lat'], data['lon']) } if 'acc' in data: kwargs['gps_accuracy'] = data['acc'] if 'batt' in data: kwargs['battery'] = data['batt'] return dev_id, kwargs
def get_new_device(event, config, device): """Add entity if not exist and the automatic_add is True.""" device_id = slugify(event.device.id_string.lower()) if device_id in RFX_DEVICES: return if not config[ATTR_AUTOMATIC_ADD]: return _LOGGER.info("Automatic add %s rfxtrx device (Class: %s Sub: %s)", device_id, event.device.__class__.__name__, event.device.subtype) pkt_id = "".join("{0:02x}".format(x) for x in event.data) datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} signal_repetitions = config[CONF_SIGNAL_REPETITIONS] new_device = device(pkt_id, event, datas, signal_repetitions) RFX_DEVICES[device_id] = new_device return new_device
def get_devices_from_config(config, device): """Read rfxtrx configuration.""" signal_repetitions = config[CONF_SIGNAL_REPETITIONS] devices = [] for packet_id, entity_info in config[CONF_DEVICES].items(): event = get_rfx_object(packet_id) device_id = slugify(event.device.id_string.lower()) if device_id in RFX_DEVICES: continue _LOGGER.info("Add %s rfxtrx", entity_info[ATTR_NAME]) # Check if i must fire event fire_event = entity_info[ATTR_FIREEVENT] datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event} new_device = device(entity_info[ATTR_NAME], event, datas, signal_repetitions) RFX_DEVICES[device_id] = new_device devices.append(new_device) return devices
def owntracks_event_update(topic, payload, qos): """MQTT event (geofences) received.""" # Docs on available data: # http://owntracks.org/booklet/tech/json/#_typetransition data = validate_payload(payload, 'transition') if not data: return if data.get('desc') is None: _LOGGER.error("Location missing from `Entering/Leaving` message - " "please turn `Share` on in OwnTracks app") return # OwnTracks uses - at the start of a beacon zone # to switch on 'hold mode' - ignore this location = slugify(data['desc'].lstrip("-")) if location.lower() == 'home': location = STATE_HOME dev_id, kwargs = _parse_see_args(topic, data) def enter_event(): """Execute enter event.""" zone = hass.states.get("zone.{}".format(location)) with LOCK: if zone is None and data.get('t') == 'b': # Not a HA zone, and a beacon so assume mobile beacons = MOBILE_BEACONS_ACTIVE[dev_id] if location not in beacons: beacons.append(location) _LOGGER.info("Added beacon %s", location) else: # Normal region regions = REGIONS_ENTERED[dev_id] if location not in regions: regions.append(location) _LOGGER.info("Enter region %s", location) _set_gps_from_zone(kwargs, location, zone) see(**kwargs) see_beacons(dev_id, kwargs) def leave_event(): """Execute leave event.""" with LOCK: regions = REGIONS_ENTERED[dev_id] if location in regions: regions.remove(location) new_region = regions[-1] if regions else None if new_region: # Exit to previous region zone = hass.states.get("zone.{}".format(new_region)) _set_gps_from_zone(kwargs, new_region, zone) _LOGGER.info("Exit to %s", new_region) see(**kwargs) see_beacons(dev_id, kwargs) else: _LOGGER.info("Exit to GPS") # Check for GPS accuracy if not ('acc' in data and max_gps_accuracy is not None and data['acc'] > max_gps_accuracy): see(**kwargs) see_beacons(dev_id, kwargs) else: _LOGGER.info("Inaccurate GPS reported") beacons = MOBILE_BEACONS_ACTIVE[dev_id] if location in beacons: beacons.remove(location) _LOGGER.info("Remove beacon %s", location) if data['event'] == 'enter': enter_event() elif data['event'] == 'leave': leave_event() else: _LOGGER.error('Misformatted mqtt msgs, _type=transition, event=%s', data['event']) return
def test_slugify(self): """Test slugify.""" self.assertEqual("test", util.slugify("T-!@#$!#@$!$est")) self.assertEqual("test_more", util.slugify("Test More")) self.assertEqual("test_more", util.slugify("Test_(More)"))
def setup_platform(hass, config, add_devices_callback, discovery_info=None): """Setup the RFXtrx platform.""" # pylint: disable=too-many-locals from RFXtrx import SensorEvent sensors = [] for packet_id, entity_info in config['devices'].items(): event = rfxtrx.get_rfx_object(packet_id) device_id = "sensor_" + slugify(event.device.id_string.lower()) if device_id in rfxtrx.RFX_DEVICES: continue _LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME]) sub_sensors = {} data_types = entity_info[ATTR_DATA_TYPE] if len(data_types) == 0: for data_type in DATA_TYPES: if data_type in event.values: data_types = [data_type] break for _data_type in data_types: new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME], _data_type) sensors.append(new_sensor) sub_sensors[_data_type] = new_sensor rfxtrx.RFX_DEVICES[device_id] = sub_sensors add_devices_callback(sensors) def sensor_update(event): """Callback for sensor updates from the RFXtrx gateway.""" if not isinstance(event, SensorEvent): return device_id = "sensor_" + slugify(event.device.id_string.lower()) if device_id in rfxtrx.RFX_DEVICES: sensors = rfxtrx.RFX_DEVICES[device_id] for key in sensors: sensors[key].event = event return # Add entity if not exist and the automatic_add is True if not config[ATTR_AUTOMATIC_ADD]: return pkt_id = "".join("{0:02x}".format(x) for x in event.data) _LOGGER.info("Automatic add rfxtrx.sensor: %s", device_id) data_type = "Unknown" for _data_type in DATA_TYPES: if _data_type in event.values: data_type = _data_type break new_sensor = RfxtrxSensor(event, pkt_id, data_type) sub_sensors = {} sub_sensors[new_sensor.data_type] = new_sensor rfxtrx.RFX_DEVICES[device_id] = sub_sensors add_devices_callback([new_sensor]) if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(sensor_update)
def owntracks_event_update(topic, payload, qos): """MQTT event (geofences) received.""" # Docs on available data: # http://owntracks.org/booklet/tech/json/#_typetransition data = validate_payload(payload, 'transition') if not data: return if data.get('desc') is None: _LOGGER.error( "Location missing from `Entering/Leaving` message - " "please turn `Share` on in OwnTracks app") return # OwnTracks uses - at the start of a beacon zone # to switch on 'hold mode' - ignore this location = slugify(data['desc'].lstrip("-")) if location.lower() == 'home': location = STATE_HOME dev_id, kwargs = _parse_see_args(topic, data) def enter_event(): """Execute enter event.""" zone = hass.states.get("zone.{}".format(location)) with LOCK: if zone is None and data.get('t') == 'b': # Not a HA zone, and a beacon so assume mobile beacons = MOBILE_BEACONS_ACTIVE[dev_id] if location not in beacons: beacons.append(location) _LOGGER.info("Added beacon %s", location) else: # Normal region regions = REGIONS_ENTERED[dev_id] if location not in regions: regions.append(location) _LOGGER.info("Enter region %s", location) _set_gps_from_zone(kwargs, location, zone) see(**kwargs) see_beacons(dev_id, kwargs) def leave_event(): """Execute leave event.""" with LOCK: regions = REGIONS_ENTERED[dev_id] if location in regions: regions.remove(location) new_region = regions[-1] if regions else None if new_region: # Exit to previous region zone = hass.states.get("zone.{}".format(new_region)) _set_gps_from_zone(kwargs, new_region, zone) _LOGGER.info("Exit to %s", new_region) see(**kwargs) see_beacons(dev_id, kwargs) else: _LOGGER.info("Exit to GPS") # Check for GPS accuracy if not ('acc' in data and max_gps_accuracy is not None and data['acc'] > max_gps_accuracy): see(**kwargs) see_beacons(dev_id, kwargs) else: _LOGGER.info("Inaccurate GPS reported") beacons = MOBILE_BEACONS_ACTIVE[dev_id] if location in beacons: beacons.remove(location) _LOGGER.info("Remove beacon %s", location) if data['event'] == 'enter': enter_event() elif data['event'] == 'leave': leave_event() else: _LOGGER.error( 'Misformatted mqtt msgs, _type=transition, event=%s', data['event']) return