def test_convert_invalid_unit(): """Test exception is thrown for invalid units.""" with pytest.raises(ValueError): distance_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL) with pytest.raises(ValueError): distance_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)
def length(self, length: float | None, from_unit: str) -> float: """Convert the given length to this unit system.""" if not isinstance(length, Number): raise TypeError(f"{length!s} is not a numeric value.") # type ignore: https://github.com/python/mypy/issues/7207 return distance_util.convert( # type: ignore length, from_unit, self.length_unit )
def test_convert_same_unit(): """Test conversion from any unit to same unit.""" assert distance_util.convert(5, LENGTH_KILOMETERS, LENGTH_KILOMETERS) == 5 assert distance_util.convert(2, LENGTH_METERS, LENGTH_METERS) == 2 assert distance_util.convert(6, LENGTH_CENTIMETERS, LENGTH_CENTIMETERS) == 6 assert distance_util.convert(3, LENGTH_MILLIMETERS, LENGTH_MILLIMETERS) == 3 assert distance_util.convert(10, LENGTH_MILES, LENGTH_MILES) == 10 assert distance_util.convert(9, LENGTH_YARD, LENGTH_YARD) == 9 assert distance_util.convert(8, LENGTH_FEET, LENGTH_FEET) == 8 assert distance_util.convert(7, LENGTH_INCHES, LENGTH_INCHES) == 7
def __init__(self, opp, name, latitude, longitude, radius, altitude): """Initialize the sensor.""" self._session = requests.Session() self._latitude = latitude self._longitude = longitude self._radius = util_distance.convert(radius, LENGTH_KILOMETERS, LENGTH_METERS) self._altitude = altitude self._state = 0 self._opp = opp self._name = name self._previously_tracked = None
async def async_setup_platform(opp, config, async_add_entities, discovery_info=None): """Set up the CityBikes platform.""" if PLATFORM not in opp.data: opp.data[PLATFORM] = {MONITORED_NETWORKS: {}} latitude = config.get(CONF_LATITUDE, opp.config.latitude) longitude = config.get(CONF_LONGITUDE, opp.config.longitude) network_id = config.get(CONF_NETWORK) stations_list = set(config.get(CONF_STATIONS_LIST, [])) radius = config.get(CONF_RADIUS, 0) name = config[CONF_NAME] if not opp.config.units.is_metric: radius = distance.convert(radius, LENGTH_FEET, LENGTH_METERS) # Create a single instance of CityBikesNetworks. networks = opp.data.setdefault(CITYBIKES_NETWORKS, CityBikesNetworks(opp)) if not network_id: network_id = await networks.get_closest_network_id(latitude, longitude) if network_id not in opp.data[PLATFORM][MONITORED_NETWORKS]: network = CityBikesNetwork(opp, network_id) opp.data[PLATFORM][MONITORED_NETWORKS][network_id] = network opp.async_create_task(network.async_refresh()) async_track_time_interval(opp, network.async_refresh, SCAN_INTERVAL) else: network = opp.data[PLATFORM][MONITORED_NETWORKS][network_id] await network.ready.wait() devices = [] for station in network.stations: dist = location.distance(latitude, longitude, station[ATTR_LATITUDE], station[ATTR_LONGITUDE]) station_id = station[ATTR_ID] station_uid = str(station.get(ATTR_EXTRA, {}).get(ATTR_UID, "")) if radius > dist or stations_list.intersection( (station_id, station_uid)): if name: uid = "_".join([network.network_id, name, station_id]) else: uid = "_".join([network.network_id, station_id]) entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, uid, opp=opp) devices.append(CityBikesStation(network, station_id, entity_id)) async_add_entities(devices, True)
def test_convert_from_meters(): """Test conversion from meters to other units.""" m = 5000 assert distance_util.convert(m, LENGTH_METERS, LENGTH_KILOMETERS) == 5 assert distance_util.convert(m, LENGTH_METERS, LENGTH_CENTIMETERS) == 500000 assert distance_util.convert(m, LENGTH_METERS, LENGTH_MILLIMETERS) == 5000000 assert distance_util.convert(m, LENGTH_METERS, LENGTH_MILES) == 3.106855 assert distance_util.convert(m, LENGTH_METERS, LENGTH_YARD) == 5468.05 assert distance_util.convert(m, LENGTH_METERS, LENGTH_FEET) == 16404.2 assert distance_util.convert(m, LENGTH_METERS, LENGTH_INCHES) == 196850.5
def test_convert_from_feet(): """Test conversion from feet to other units.""" feet = 5000 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_KILOMETERS) == 1.524 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_METERS) == 1524 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_CENTIMETERS) == 152400.0 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_MILLIMETERS) == 1524000.0 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_MILES) == 0.9469694040000001 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_YARD) == 1666.66164 assert distance_util.convert(feet, LENGTH_FEET, LENGTH_INCHES) == 60000.032400000004
def test_convert_from_yards(): """Test conversion from yards to other units.""" yards = 5 assert (distance_util.convert(yards, LENGTH_YARD, LENGTH_KILOMETERS) == 0.0045720000000000005) assert distance_util.convert(yards, LENGTH_YARD, LENGTH_METERS) == 4.572 assert distance_util.convert(yards, LENGTH_YARD, LENGTH_CENTIMETERS) == 457.2 assert distance_util.convert(yards, LENGTH_YARD, LENGTH_MILLIMETERS) == 4572.0 assert distance_util.convert(yards, LENGTH_YARD, LENGTH_MILES) == 0.002840908212 assert distance_util.convert(yards, LENGTH_YARD, LENGTH_FEET) == 15.00000048 assert distance_util.convert(yards, LENGTH_YARD, LENGTH_INCHES) == 180.0000972
def test_convert_from_miles(): """Test conversion from miles to other units.""" miles = 5 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_KILOMETERS) == 8.04672 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_METERS) == 8046.72 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_CENTIMETERS) == 804672.0 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_MILLIMETERS) == 8046720.0 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_YARD) == 8799.9734592 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_FEET) == 26400.0008448 assert distance_util.convert(miles, LENGTH_MILES, LENGTH_INCHES) == 316800.171072
def test_convert_from_inches(): """Test conversion from inches to other units.""" inches = 5000 assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_KILOMETERS) == 0.127 assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_METERS) == 127.0 assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_CENTIMETERS) == 12700.0 assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_MILLIMETERS) == 127000.0 assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_MILES) == 0.078914117 assert (distance_util.convert(inches, LENGTH_INCHES, LENGTH_YARD) == 138.88846999999998) assert distance_util.convert(inches, LENGTH_INCHES, LENGTH_FEET) == 416.66668
def state(self) -> float | None: """Return the state of the sensor.""" if self.tesla_device.type == "temperature sensor": if self.type == "outside": return self.tesla_device.get_outside_temp() return self.tesla_device.get_inside_temp() if self.tesla_device.type in ["range sensor", "mileage sensor"]: units = self.tesla_device.measurement if units == "LENGTH_MILES": return self.tesla_device.get_value() return round( convert(self.tesla_device.get_value(), LENGTH_MILES, LENGTH_KILOMETERS), 2, ) if self.tesla_device.type == "charging rate sensor": return self.tesla_device.charging_rate return self.tesla_device.get_value()
def check_proximity_state_change(self, entity, old_state, new_state): """Perform the proximity checking.""" entity_name = new_state.name devices_to_calculate = False devices_in_zone = "" zone_state = self.opp.states.get(self.proximity_zone) proximity_latitude = zone_state.attributes.get(ATTR_LATITUDE) proximity_longitude = zone_state.attributes.get(ATTR_LONGITUDE) # Check for devices in the monitored zone. for device in self.proximity_devices: device_state = self.opp.states.get(device) if device_state is None: devices_to_calculate = True continue if device_state.state not in self.ignored_zones: devices_to_calculate = True # Check the location of all devices. if (device_state.state).lower() == (self.friendly_name).lower(): device_friendly = device_state.name if devices_in_zone != "": devices_in_zone = f"{devices_in_zone}, " devices_in_zone = devices_in_zone + device_friendly # No-one to track so reset the entity. if not devices_to_calculate: self.dist_to = "not set" self.dir_of_travel = "not set" self.nearest = "not set" self.schedule_update_op_state() return # At least one device is in the monitored zone so update the entity. if devices_in_zone != "": self.dist_to = 0 self.dir_of_travel = "arrived" self.nearest = devices_in_zone self.schedule_update_op_state() return # We can't check proximity because latitude and longitude don't exist. if "latitude" not in new_state.attributes: return # Collect distances to the zone for all devices. distances_to_zone = {} for device in self.proximity_devices: # Ignore devices in an ignored zone. device_state = self.opp.states.get(device) if device_state.state in self.ignored_zones: continue # Ignore devices if proximity cannot be calculated. if "latitude" not in device_state.attributes: continue # Calculate the distance to the proximity zone. dist_to_zone = distance( proximity_latitude, proximity_longitude, device_state.attributes[ATTR_LATITUDE], device_state.attributes[ATTR_LONGITUDE], ) # Add the device and distance to a dictionary. distances_to_zone[device] = round( convert(dist_to_zone, LENGTH_METERS, self.unit_of_measurement), 1) # Loop through each of the distances collected and work out the # closest. closest_device: str = None dist_to_zone: float = None for device in distances_to_zone: if not dist_to_zone or distances_to_zone[device] < dist_to_zone: closest_device = device dist_to_zone = distances_to_zone[device] # If the closest device is one of the other devices. if closest_device != entity: self.dist_to = round(distances_to_zone[closest_device]) self.dir_of_travel = "unknown" device_state = self.opp.states.get(closest_device) self.nearest = device_state.name self.schedule_update_op_state() return # Stop if we cannot calculate the direction of travel (i.e. we don't # have a previous state and a current LAT and LONG). if old_state is None or "latitude" not in old_state.attributes: self.dist_to = round(distances_to_zone[entity]) self.dir_of_travel = "unknown" self.nearest = entity_name self.schedule_update_op_state() return # Reset the variables distance_travelled = 0 # Calculate the distance travelled. old_distance = distance( proximity_latitude, proximity_longitude, old_state.attributes[ATTR_LATITUDE], old_state.attributes[ATTR_LONGITUDE], ) new_distance = distance( proximity_latitude, proximity_longitude, new_state.attributes[ATTR_LATITUDE], new_state.attributes[ATTR_LONGITUDE], ) distance_travelled = round(new_distance - old_distance, 1) # Check for tolerance if distance_travelled < self.tolerance * -1: direction_of_travel = "towards" elif distance_travelled > self.tolerance: direction_of_travel = "away_from" else: direction_of_travel = "stationary" # Update the proximity entity self.dist_to = round(dist_to_zone) self.dir_of_travel = direction_of_travel self.nearest = entity_name self.schedule_update_op_state() _LOGGER.debug( "proximity.%s update entity: distance=%s: direction=%s: device=%s", self.friendly_name, round(dist_to_zone), direction_of_travel, entity_name, ) _LOGGER.info("%s: proximity calculation complete", entity_name)
def test_convert_nonnumeric_value(): """Test exception is thrown for nonnumeric type.""" with pytest.raises(TypeError): distance_util.convert("a", LENGTH_KILOMETERS, LENGTH_METERS)
def _update_member(self, member, dev_id): loc = member.get("location") try: last_seen = _utc_from_ts(loc.get("timestamp")) except AttributeError: last_seen = None prev_seen = self._prev_seen(dev_id, last_seen) if not loc: err_msg = member["issues"]["title"] if err_msg: if member["issues"]["dialog"]: err_msg += f": {member['issues']['dialog']}" else: err_msg = "Location information missing" self._err(dev_id, err_msg) return # Only update when we truly have an update. if not last_seen: _LOGGER.warning("%s: Ignoring update because timestamp is missing", dev_id) return if prev_seen and last_seen < prev_seen: _LOGGER.warning( "%s: Ignoring update because timestamp is older than last timestamp", dev_id, ) _LOGGER.debug("%s < %s", last_seen, prev_seen) return if last_seen == prev_seen: return lat = loc.get("latitude") lon = loc.get("longitude") gps_accuracy = loc.get("accuracy") try: lat = float(lat) lon = float(lon) # Life360 reports accuracy in feet, but Device Tracker expects # gps_accuracy in meters. gps_accuracy = round( convert(float(gps_accuracy), LENGTH_FEET, LENGTH_METERS)) except (TypeError, ValueError): self._err(dev_id, f"GPS data invalid: {lat}, {lon}, {gps_accuracy}") return self._ok(dev_id) msg = f"Updating {dev_id}" if prev_seen: msg += f"; Time since last update: {last_seen - prev_seen}" _LOGGER.debug(msg) if self._max_gps_accuracy is not None and gps_accuracy > self._max_gps_accuracy: _LOGGER.warning( "%s: Ignoring update because expected GPS " "accuracy (%.0f) is not met: %.0f", dev_id, self._max_gps_accuracy, gps_accuracy, ) return # Get raw attribute data, converting empty strings to None. place = loc.get("name") or None address1 = loc.get("address1") or None address2 = loc.get("address2") or None if address1 and address2: address = ", ".join([address1, address2]) else: address = address1 or address2 raw_speed = loc.get("speed") or None driving = _bool_attr_from_int(loc.get("isDriving")) moving = _bool_attr_from_int(loc.get("inTransit")) try: battery = int(float(loc.get("battery"))) except (TypeError, ValueError): battery = None # Try to convert raw speed into real speed. try: speed = float(raw_speed) * SPEED_FACTOR_MPH if self._opp.config.units.is_metric: speed = convert(speed, LENGTH_MILES, LENGTH_KILOMETERS) speed = max(0, round(speed)) except (TypeError, ValueError): speed = STATE_UNKNOWN # Make driving attribute True if it isn't and we can derive that it # should be True from other data. if (driving in (STATE_UNKNOWN, False) and self._driving_speed is not None and speed != STATE_UNKNOWN): driving = speed >= self._driving_speed attrs = { ATTR_ADDRESS: address, ATTR_AT_LOC_SINCE: _dt_attr_from_ts(loc.get("since")), ATTR_BATTERY_CHARGING: _bool_attr_from_int(loc.get("charge")), ATTR_DRIVING: driving, ATTR_LAST_SEEN: last_seen, ATTR_MOVING: moving, ATTR_PLACE: place, ATTR_RAW_SPEED: raw_speed, ATTR_SPEED: speed, ATTR_WIFI_ON: _bool_attr_from_int(loc.get("wifiState")), } # If user wants driving or moving to be shown as state, and current # location is not in a OPP zone, then set location name accordingly. loc_name = None active_zone = run_callback_threadsafe(self._opp.loop, async_active_zone, self._opp, lat, lon, gps_accuracy).result() if not active_zone: if SHOW_DRIVING in self._show_as_state and driving is True: loc_name = SHOW_DRIVING elif SHOW_MOVING in self._show_as_state and moving is True: loc_name = SHOW_MOVING self._see( dev_id=dev_id, location_name=loc_name, gps=(lat, lon), gps_accuracy=gps_accuracy, battery=battery, attributes=attrs, picture=member.get("avatar"), )