Sensorki – Serverless app i refaktoring – #9

Postanowiłam zmienić format jsona, którego obrabiam na androidzie, na bardziej przystępny. Tak powstała aplikacja w chmurze google’a, która pozwoliła uprościć kod.

{
"data": {
"hum_temp": "Temperatura (\u00b0C)",
"hum_hum": "Wilgotno\u015b\u0107 (%)",
"bar_pres_rel": "Ci\u015bnienie (hPa)",
"lux": "Jasno\u015b\u0107 (lx)",
"vbat": "Nap. baterii (V)",
"vreg": "Nap. sensor\u00f3w (V)"
},
"sensors": {
"1": { "label": "Balkon", "data": ["hum_temp","hum_hum","bar_pres_rel","lux","vbat","vreg"] },
"2": { "label": "Salon", "data": ["hum_temp","hum_hum","lux","vbat","vreg" ] },
"4": { "label": "Biuro", "data": ["hum_temp","hum_hum","lux","vbat","vreg" ] },
"5": { "label": "\u0141azienka", "data": ["hum_temp","hum_hum","lux","vbat","vreg" ] }
},
"readings": {
"1": { "id": 3426091, "stamp": 1549810634, "sensor_id": 1, "seq": 45116, "status": 0, "flags": 23, "bar_temp": 8.22, "bar_pres_abs": 985.472, "bar_pres_rel": 1001.16, "lux": 293.8, "hum_temp": 7.76, "hum_hum": 111.8, "vbat": 3.76, "vreg": 2.85 },
"2": { "id": 3426089, "stamp": 1549810629, "sensor_id": 2, "seq": 31088, "status": 0, "flags": 6, "bar_temp": null, "bar_pres_abs": null, "bar_pres_rel": null, "lux": 5.72344, "hum_temp": 24.01, "hum_hum": 51.52, "vbat": 3.64, "vreg": 2.76 },
"4": { "id": 3426088, "stamp": 1549810626, "sensor_id": 4, "seq": 49955, "status": 0, "flags": 22, "bar_temp": null, "bar_pres_abs": null, "bar_pres_rel": null, "lux": 15.9223, "hum_temp": 23.38, "hum_hum": 54.3, "vbat": 3.7, "vreg": 2.84 },
"5": { "id": 3426090, "stamp": 1549810633, "sensor_id": 5, "seq": 47950, "status": 0, "flags": 22, "bar_temp": null, "bar_pres_abs": null, "bar_pres_rel": null, "lux": 0.0259, "hum_temp": 26.18, "hum_hum": 51.63, "vbat": 3.67, "vreg": 2.79 }
}
}

Format JSON-a zawierający odczyty sensorka nie był zbyt przyjazny do wyświetlania przez aplikację. Trzy obiekty zawierające kolejne obiekty. Dużo danych, których wcale nie potrzebowałam. Żeby wyciągnąć etykietę z sensora musiałam się odwołać do innej listy.

Postanowiłam sprowadzić tego JSON-a do prostej listy zawierającej tylko te dane, z których naprawdę korzystała. Za pomocą javaScriptu i node.js stworzyłam funkcję pobierajcą jsona z dotychczasowego endpointu. Dwie pętle i JSON zaczął być maksymalnie użyteczny.

const https = require("https");
const http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'application/json'});
const req = https.request('https://xxxxxxxxxxxxx.com/data.json', (res) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
const data = JSON.parse(chunk);
const sensors = [];
for (const id in data.readings) {
const sensor = {};
const sourceSensor = data.sensors[id];
sensor.label = sourceSensor.label;
const readings = data.readings[id];
for (const k of ["hum_temp", "stamp", "hum_hum", "lux", "bar_pres_rel", "vbat", "vreg", "id"]) {
if (k === 'stamp') {
sensor[k] = readings[k] * 1000
} else {
sensor[k] = readings[k]
}
}
sensors.push(sensor);
}
response.end(JSON.stringify(sensors));
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.end();
}).listen(8081);
view raw app.js hosted with ❤ by GitHub

Wyodrębniłam z serwera funkcję i przeniosłam do chmury google’a tworząc “aplikację bezserwerową.

W wyniku obróbki przez funkcję dostaję teraz takiego JSON-a.

[
{ "label": "Balkon", "id": 3426099, "hum_temp": 7.7, "stamp": 1549810672000, "hum_hum": 111.72, "lux": 291.902, "bar_pres_rel": 1001.12, "vbat": 3.75, "vreg": 2.84 },
{ "label": "Salon", "id": 3426097, "hum_temp": 24, "stamp": 1549810663000, "hum_hum": 51.31, "lux": 5.74635, "bar_pres_rel": null, "vbat": 3.64, "vreg": 2.76 },
{ "label": "Biuro", "id": 3426096, "hum_temp": 23.38, "stamp": 1549810661000, "hum_hum": 54.19, "lux": 15.9444, "bar_pres_rel": null, "vbat": 3.7, "vreg": 2.84 },
{ "label": "Łazienka", "id": 3426098, "hum_temp": 26.17, "stamp": 1549810668000, "hum_hum": 51.62, "lux": 0.0259, "bar_pres_rel": null, "vbat": 3.67, "vreg": 2.79 }
]

Pozwoliło mi to na użycie biblioteki Moshi do mapowania JSONa prosto do javowej klasy. Adnotacje w miejscach gdzie nazwa pola nie zgadzała się z polem w JSONie

public class Sensor {
private long id;
private String label;
@Json(name = "hum_temp")
private double temperature;
@Json(name = "stamp")
private long timestamp;
@Json(name = "hum_hum")
private double humidity;
private double lux;
@Json(name = "bar_pres_rel")
private Double barPressure;
private double vbat;
private double vreg;
}
view raw Sensor.java hosted with ❤ by GitHub

i już mogę korzystać z tego, że serwer zwraca mi po prostu listę obiektów Sensor zamiast JSON Object

public interface WeatherService {
@GET("sensorFriendly")
Call<List<Sensor>> loadData();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.WEATHER_ADDRESS)
.addConverterFactory(MoshiConverterFactory.create())
.build();
}

Pozbywam się ręcznego wydobywania z JSON-a labelek i poszczególnych odczytów.

private void sendQueryForData() {
final Context mContext = this;
WeatherService weatherService = WeatherService.retrofit.create(WeatherService.class);
final Call<List<Sensor>> call = weatherService.loadData();
call.enqueue(new Callback<List<Sensor>>() {
@Override
public void onResponse(@NonNull Call<List<Sensor>> call, @NonNull Response<List<Sensor>> response) {
List<Sensor> sensors = response.body();
if (sensors != null) {
Timber.i("Server response with: %s", sensors);
adapter.updateResults(sensors);
notificationService.checkSensorsState(sensors, mContext);
}
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onFailure(@NonNull Call<List<Sensor>> call, @NonNull Throwable t) {
swipeRefreshLayout.setRefreshing(false);
Timber.e(t.getLocalizedMessage());
Toast.makeText(mContext, R.string.connection_error, Toast.LENGTH_LONG).show();
}
});
}

Działanie aplikacji nie zmieniło się wcale, ale kod jest o wiele bardziej czytelny.

Series Navigation<< Sensorki – podsumowanie – #8

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

%d bloggers like this: