Пример #1
0
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)
Пример #2
0
    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
        )
Пример #3
0
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
Пример #4
0
 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
Пример #5
0
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)
Пример #6
0
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
Пример #7
0
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
Пример #8
0
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
Пример #9
0
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
Пример #10
0
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
Пример #11
0
 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()
Пример #12
0
    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)
Пример #13
0
def test_convert_nonnumeric_value():
    """Test exception is thrown for nonnumeric type."""
    with pytest.raises(TypeError):
        distance_util.convert("a", LENGTH_KILOMETERS, LENGTH_METERS)
Пример #14
0
    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"),
        )