mirror of
https://github.com/ev-map/EVMap.git
synced 2026-04-24 08:07:09 -04:00
implement AvailabilityDetector using chargecloud API (#1)
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package com.johan.evmap.api
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import okhttp3.*
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val radius = 100 // max radius in meters
|
||||
|
||||
interface AvailabilityDetector {
|
||||
suspend fun getAvailability(location: ChargeLocation): Map<Chargepoint, List<ChargepointStatus>>
|
||||
}
|
||||
|
||||
enum class ChargepointStatus {
|
||||
AVAILABLE, UNKNOWN, CHARGING
|
||||
}
|
||||
|
||||
class ChargecloudAvailabilityDetector(private val client: OkHttpClient,
|
||||
private val operatorId: String): AvailabilityDetector {
|
||||
@ExperimentalCoroutinesApi
|
||||
override suspend fun getAvailability(location: ChargeLocation): Map<Chargepoint, List<ChargepointStatus>> {
|
||||
val url = "https://app.chargecloud.de/emobility:ocpi/$operatorId/app/2.0/locations?latitude=${location.coordinates.lat}&longitude=${location.coordinates.lng}&radius=$radius&offset=0&limit=10"
|
||||
val request = Request.Builder().url(url).build()
|
||||
val response = client.newCall(request).await()
|
||||
|
||||
if (!response.isSuccessful) throw IOException(response.message())
|
||||
|
||||
val json = JSONObject(response.body()!!.string())
|
||||
|
||||
val statusMessage = json.getString("status_message")
|
||||
if (statusMessage != "Success") throw IOException(statusMessage)
|
||||
|
||||
val data = json.getJSONArray("data")
|
||||
if (data.length() > 1) throw IOException("found multiple candidates.")
|
||||
if (data.length() == 0) throw IOException("no candidates found.")
|
||||
|
||||
val evses = data.getJSONObject(0).getJSONArray("evses")
|
||||
val chargepointStatus = mutableMapOf<Chargepoint, List<ChargepointStatus>>()
|
||||
evses.iterator<JSONObject>().forEach { evse ->
|
||||
evse.getJSONArray("connectors").iterator<JSONObject>().forEach connector@{ connector ->
|
||||
val type = getType(connector.getString("standard"))
|
||||
val power = connector.getDouble("max_power")
|
||||
val status = ChargepointStatus.valueOf(connector.getString("status"))
|
||||
if (type == null) return@connector
|
||||
|
||||
var chargepoint = chargepointStatus.keys.filter {
|
||||
it.type == type
|
||||
it.power == power
|
||||
}.getOrNull(0)
|
||||
val statusList: List<ChargepointStatus>
|
||||
if (chargepoint == null) {
|
||||
chargepoint = Chargepoint(type, power, 1)
|
||||
statusList = listOf(status)
|
||||
} else {
|
||||
val previousStatus = chargepointStatus[chargepoint]!!
|
||||
statusList = previousStatus + listOf(status)
|
||||
chargepointStatus.remove(chargepoint)
|
||||
chargepoint =
|
||||
Chargepoint(chargepoint.type, chargepoint.power, chargepoint.count + 1)
|
||||
}
|
||||
|
||||
chargepointStatus[chargepoint] = statusList
|
||||
}
|
||||
}
|
||||
return chargepointStatus
|
||||
}
|
||||
|
||||
private fun getType(string: String): String? {
|
||||
return when (string) {
|
||||
"IEC_62196_T2" -> Chargepoint.TYPE_2
|
||||
"DOMESTIC_F" -> Chargepoint.SCHUKO
|
||||
"IEC_62196_T2_COMBO" -> Chargepoint.CCS
|
||||
"CHADEMO" -> Chargepoint.CHADEMO
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val okhttp = OkHttpClient.Builder()
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
val availabilityDetectors = listOf(
|
||||
ChargecloudAvailabilityDetector(okhttp, "6336fe713f2eb7fa04b97ff6651b76f8")
|
||||
)
|
||||
@@ -179,4 +179,12 @@ data class Chargepoint(val type: String, val power: Double, val count: Int) : Eq
|
||||
}
|
||||
return "$powerFmt kW"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_2 = "Type2"
|
||||
const val CCS = "CCS"
|
||||
const val SCHUKO = "Schuko"
|
||||
const val CHADEMO = "CHAdeMO"
|
||||
const val SUPERCHARGER = "Tesla Supercharger"
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/com/johan/evmap/api/Utils.kt
Normal file
38
app/src/main/java/com/johan/evmap/api/Utils.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.johan.evmap.api
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
operator fun<T> JSONArray.iterator(): Iterator<T>
|
||||
= (0 until length()).asSequence().map { get(it) as T }.iterator()
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
suspend fun Call.await(): Response {
|
||||
return suspendCancellableCoroutine { continuation ->
|
||||
enqueue(object : Callback {
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
continuation.resume(response) {}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
if (continuation.isCancelled) return
|
||||
continuation.resumeWithException(e)
|
||||
}
|
||||
})
|
||||
|
||||
continuation.invokeOnCancellation {
|
||||
try {
|
||||
cancel()
|
||||
} catch (ex: Throwable) {
|
||||
//Ignore cancel exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.johan.evmap.R
|
||||
import com.johan.evmap.api.Chargepoint
|
||||
|
||||
|
||||
@BindingAdapter("goneUnless")
|
||||
@@ -51,11 +52,11 @@ fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
|
||||
fun getConnectorItem(view: ImageView, type: String) {
|
||||
view.setImageResource(
|
||||
when (type) {
|
||||
"CCS" -> R.drawable.ic_connector_ccs
|
||||
"CHAdeMO" -> R.drawable.ic_connector_chademo
|
||||
"Schuko" -> R.drawable.ic_connector_schuko
|
||||
"Tesla Supercharger" -> R.drawable.ic_connector_supercharger
|
||||
"Typ2" -> R.drawable.ic_connector_typ2
|
||||
Chargepoint.CCS -> R.drawable.ic_connector_ccs
|
||||
Chargepoint.CHADEMO -> R.drawable.ic_connector_chademo
|
||||
Chargepoint.SCHUKO -> R.drawable.ic_connector_schuko
|
||||
Chargepoint.SUPERCHARGER -> R.drawable.ic_connector_supercharger
|
||||
Chargepoint.TYPE_2 -> R.drawable.ic_connector_typ2
|
||||
// TODO: add other connectors
|
||||
else -> 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user