001/* 002 * geordi 003 * 004 * Copyright (C) 2018 Richard "Shred" Körber 005 * https://github.com/shred/geordi 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as 009 * published by the Free Software Foundation, either version 3 of the 010 * License, or (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 015 */ 016package org.shredzone.geordi.device; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.math.BigDecimal; 021import java.net.MalformedURLException; 022import java.net.URL; 023import java.time.Instant; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.List; 027 028import javax.inject.Inject; 029 030import org.json.JSONArray; 031import org.json.JSONException; 032import org.json.JSONObject; 033import org.json.JSONTokener; 034import org.shredzone.geordi.GeordiException; 035import org.shredzone.geordi.data.Sample; 036import org.shredzone.geordi.sensor.Sensor; 037import org.shredzone.geordi.service.DatabaseService; 038 039/** 040 * A {@link Device} implementation that reads Aquaero fan controllers. It 041 * requires a running <a href="https://github.com/shred/pyquaero">Pyquaero</a> server that 042 * is connected to the Aquaero device. 043 * 044 * @see <a href="https://aquacomputer.de">Aqua Computer GmbH & Co. KG</a> 045 */ 046public class AquaeroDevice extends Device { 047 048 @Inject 049 private DatabaseService databaseService; 050 051 @Override 052 public List<Sample> readSensors() { 053 JSONObject json; 054 try (InputStream in = getServerUrl().openStream()) { 055 json = new JSONObject(new JSONTokener(in)); 056 } catch (IOException | JSONException ex) { 057 throw new GeordiException("Could not read data for sensor " + getId(), ex); 058 } 059 060 Instant ts = Instant.parse(json.getString("time") + "Z"); 061 062 List<Sample> result = new ArrayList<>(); 063 for (Sensor sensor : databaseService.fetchSensors(this)) { 064 BigDecimal value = getSensorValue(json, sensor); 065 066 // If Pyquaero runs on a Raspberry Pi 1, there might be a misreading of 067 // the sensors due to a hardware bug. We will ignore the 0 value that is 068 // returned from a misreading. 069 if (BigDecimal.ZERO.equals(value)) { 070 return Collections.emptyList(); 071 } 072 073 result.add(new Sample(sensor, ts, value)); 074 } 075 076 return result; 077 } 078 079 /** 080 * Gets the {@link Sensor} value from the JSON data. 081 * 082 * @param json 083 * JSON data of Pyquaero 084 * @param sensor 085 * {@link Sensor} to read 086 * @return Value that was read 087 */ 088 private BigDecimal getSensorValue(JSONObject json, Sensor sensor) { 089 JSONObject config = sensor.getConfig(); 090 JSONArray data = locate(json, config.getString("type")); 091 JSONObject values = data.getJSONObject(config.getInt("index")); 092 return values.getBigDecimal(config.getString("value")); 093 } 094 095 /** 096 * Locates a JSON array containing sensor data. 097 * 098 * @param json 099 * JSON data of pyquaero 100 * @param path 101 * Path to the array 102 * @return JSON array 103 */ 104 private JSONArray locate(JSONObject json, String path) { 105 String[] parts = path.split("[/.]"); 106 JSONObject current = json; 107 for (int ix = 0; ix < parts.length - 1; ix++) { 108 current = current.getJSONObject(parts[ix]); 109 } 110 return current.getJSONArray(parts[parts.length - 1]); 111 } 112 113 /** 114 * Returns the URL of the Pyquaero server. 115 */ 116 private URL getServerUrl() { 117 try { 118 return new URL(String.format("http://%s:%d/status", 119 getConfig().getString("host"), 120 getConfig().getInt("port"))); 121 } catch (MalformedURLException | JSONException ex) { 122 throw new GeordiException("Bad host config", ex); 123 } 124 } 125 126}