mirror of
https://github.com/ev-map/EVMap.git
synced 2026-06-10 23:38:25 -04:00
Remove NewMotionAvailabilityDetector
old Shell Recharge/NewMotion map doesn't exist anymore, the new one is limited to Shell's own chargers
This commit is contained in:
@@ -176,8 +176,7 @@ class AvailabilityRepository(context: Context) {
|
||||
teslaOwnerAvailabilityDetector,
|
||||
TeslaGuestAvailabilityDetector(okhttp),
|
||||
NobilAvailabilityDetector(okhttp, context),
|
||||
EnBwAvailabilityDetector(okhttp),
|
||||
NewMotionAvailabilityDetector(okhttp)
|
||||
EnBwAvailabilityDetector(okhttp)
|
||||
)
|
||||
|
||||
suspend fun getAvailability(charger: ChargeLocation): Resource<ChargeLocationStatus> {
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
package net.vonforst.evmap.api.availability
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.ToJson
|
||||
import net.vonforst.evmap.model.ChargeLocation
|
||||
import net.vonforst.evmap.model.Chargepoint
|
||||
import net.vonforst.evmap.utils.distanceBetween
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.Locale
|
||||
|
||||
private const val coordRange = 0.005 // range of latitude and longitude for loading the map
|
||||
private const val maxDistance = 60 // max distance between reported positions in meters
|
||||
|
||||
interface NewMotionApi {
|
||||
@GET("markers/{lngMin}/{lngMax}/{latMin}/{latMax}/{zoom}")
|
||||
suspend fun getMarkers(
|
||||
@Path("lngMin") lngMin: Double,
|
||||
@Path("lngMax") lngMax: Double,
|
||||
@Path("latMin") latMin: Double,
|
||||
@Path("latMax") latMax: Double,
|
||||
@Path("zoom") zoom: Int = 22
|
||||
): List<NMMarker>
|
||||
|
||||
@GET("locations/{id}")
|
||||
suspend fun getLocation(@Path("id") id: Long): NMLocation
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long, val evseCount: Int)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMCoordinates(val latitude: Double, val longitude: Double)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMLocation(
|
||||
val uid: Long,
|
||||
val coordinates: NMCoordinates,
|
||||
val operatorName: String,
|
||||
val evses: List<NMEvse>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMEvse(
|
||||
val evseId: String?,
|
||||
val status: String,
|
||||
val connectors: List<NMConnector>,
|
||||
val updated: ZonedDateTime?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMConnector(
|
||||
val uid: Long,
|
||||
val connectorType: String,
|
||||
val electricalProperties: NMElectricalProperties
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMElectricalProperties(val powerType: String, val voltage: Int, val amperage: Int, val maxElectricPower: Double?) {
|
||||
fun getPower(): Double {
|
||||
maxElectricPower?.let { return it }
|
||||
val phases = when (powerType) {
|
||||
"AC1Phase" -> 1
|
||||
"AC3Phase" -> 3
|
||||
else -> 1
|
||||
}
|
||||
val volt = when (voltage) {
|
||||
277 -> 230
|
||||
else -> voltage
|
||||
}
|
||||
val power = volt * amperage * phases
|
||||
return when (power) {
|
||||
3680 -> 3.7
|
||||
11040 -> 11.0
|
||||
22080 -> 22.0
|
||||
43470 -> 43.0
|
||||
else -> power / 1000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(client: OkHttpClient, baseUrl: String? = null): NewMotionApi {
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl(baseUrl ?: "https://ui-map.shellrecharge.com/api/map/v2/")
|
||||
.addConverterFactory(
|
||||
MoshiConverterFactory.create(
|
||||
Moshi.Builder().add(ZonedDateTimeAdapter()).build()
|
||||
)
|
||||
)
|
||||
.client(client)
|
||||
.build()
|
||||
return retrofit.create(NewMotionApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ZonedDateTimeAdapter {
|
||||
@FromJson
|
||||
fun fromJson(value: String): ZonedDateTime? = ZonedDateTime.parse(value)
|
||||
|
||||
@ToJson
|
||||
fun toJson(value: ZonedDateTime): String = value.toString()
|
||||
}
|
||||
|
||||
data class NmStatus(
|
||||
val conn: NewMotionApi.NMConnector,
|
||||
val status: String,
|
||||
val evseId: String?,
|
||||
val updated: ZonedDateTime?
|
||||
)
|
||||
|
||||
class NewMotionAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
|
||||
BaseAvailabilityDetector(client) {
|
||||
val api = NewMotionApi.create(client, baseUrl)
|
||||
|
||||
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
|
||||
val lat = location.coordinates.lat
|
||||
val lng = location.coordinates.lng
|
||||
|
||||
// find nearest station to this position
|
||||
var markers =
|
||||
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
|
||||
val nearest = markers.minByOrNull { marker ->
|
||||
distanceBetween(marker.coordinates.latitude, marker.coordinates.longitude, lat, lng)
|
||||
} ?: throw AvailabilityDetectorException("no candidates found.")
|
||||
|
||||
if (distanceBetween(
|
||||
nearest.coordinates.latitude,
|
||||
nearest.coordinates.longitude,
|
||||
lat,
|
||||
lng
|
||||
) > radius
|
||||
) {
|
||||
throw AvailabilityDetectorException("no candidates found")
|
||||
}
|
||||
|
||||
markers = if (nearest.evseCount < location.totalChargepoints) {
|
||||
// combine related stations
|
||||
markers.filter { marker ->
|
||||
distanceBetween(
|
||||
marker.coordinates.latitude,
|
||||
marker.coordinates.longitude,
|
||||
nearest.coordinates.latitude,
|
||||
nearest.coordinates.longitude
|
||||
) < maxDistance
|
||||
}
|
||||
} else {
|
||||
listOf(nearest)
|
||||
}
|
||||
|
||||
// load details
|
||||
var details = markers.map {
|
||||
api.getLocation(it.locationUid)
|
||||
}
|
||||
// only include stations from same operator
|
||||
details = details.filter {
|
||||
it.operatorName == details[0].operatorName
|
||||
}
|
||||
val connectorStatus = details.flatMap { it.evses }.flatMap { evse ->
|
||||
evse.connectors.map { connector ->
|
||||
NmStatus(connector, evse.status, evse.evseId, evse.updated)
|
||||
}
|
||||
}
|
||||
|
||||
val nmConnectors = mutableMapOf<Long, Pair<Double, String>>()
|
||||
val nmStatus = mutableMapOf<Long, ChargepointStatus>()
|
||||
val nmEvseId = mutableMapOf<Long, String>()
|
||||
val nmUpdated = mutableMapOf<Long, ZonedDateTime>()
|
||||
connectorStatus.forEach { (connector, statusStr, evseId, updated) ->
|
||||
val id = connector.uid
|
||||
val power = connector.electricalProperties.getPower()
|
||||
val type = when (connector.connectorType.lowercase(Locale.ROOT)) {
|
||||
"type3" -> Chargepoint.TYPE_3C
|
||||
"type2" -> Chargepoint.TYPE_2_UNKNOWN
|
||||
"type1" -> Chargepoint.TYPE_1
|
||||
"domestic" -> Chargepoint.SCHUKO
|
||||
"type1combo" -> Chargepoint.CCS_TYPE_1 // US CCS, aka type1_combo
|
||||
"type2combo" -> Chargepoint.CCS_TYPE_2 // EU CCS, aka type2_combo
|
||||
"tepcochademo" -> Chargepoint.CHADEMO
|
||||
"unspecified" -> "unknown"
|
||||
"unknown" -> "unknown"
|
||||
"saej1772" -> "unknown"
|
||||
else -> "unknown"
|
||||
}
|
||||
val status = when (statusStr) {
|
||||
"Unavailable" -> ChargepointStatus.FAULTED
|
||||
"Available" -> ChargepointStatus.AVAILABLE
|
||||
"Occupied" -> ChargepointStatus.CHARGING
|
||||
"Unspecified" -> ChargepointStatus.UNKNOWN
|
||||
else -> ChargepointStatus.UNKNOWN
|
||||
}
|
||||
nmConnectors.put(id, power to type)
|
||||
nmStatus.put(id, status)
|
||||
evseId?.let { nmEvseId[id] = it }
|
||||
updated?.let { nmUpdated[id] = it }
|
||||
}
|
||||
|
||||
val match = matchChargepoints(nmConnectors, location.chargepointsMerged)
|
||||
val chargepointStatus = match.mapValues { entry ->
|
||||
entry.value.map { nmStatus[it]!! }
|
||||
}
|
||||
val evseIds = if (nmEvseId.size == nmStatus.size) match.mapValues { entry ->
|
||||
entry.value.map { nmEvseId[it]!! }
|
||||
} else null
|
||||
val updated = match.mapValues { entry -> entry.value.map { nmUpdated[it]?.toInstant() } }
|
||||
return ChargeLocationStatus(
|
||||
chargepointStatus,
|
||||
"NewMotion",
|
||||
evseIds,
|
||||
lastChange = updated
|
||||
)
|
||||
}
|
||||
|
||||
override fun isChargerSupported(charger: ChargeLocation): Boolean {
|
||||
// NewMotion is our fallback
|
||||
return when (charger.dataSource) {
|
||||
"goingelectric" -> charger.network != "Tesla Supercharger"
|
||||
"nobil" -> charger.network != "Tesla"
|
||||
"openchargemap" -> charger.chargepriceData?.network !in listOf("23", "3534")
|
||||
"openstreetmap" -> charger.operator !in listOf("Tesla, Inc.", "Tesla")
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user