From bafa841390a6ecb093855a8cc577e39cd18a0c0f Mon Sep 17 00:00:00 2001 From: Bernd Weymann Date: Thu, 13 May 2021 16:26:17 +0200 Subject: [PATCH] [luftdateninfo] Add internal sensor support (#10643) Signed-off-by: Bernd Weymann --- .../README.md | 19 ++++++-- .../doc/local-sensor.png | Bin 0 -> 51060 bytes .../doc/logo-rund.png | Bin 6398 -> 0 bytes .../internal/LuftdatenInfoConfiguration.java | 5 +- .../internal/LuftdatenInfoHandlerFactory.java | 8 +-- .../internal/handler/BaseSensorHandler.java | 36 +++++++++++--- .../internal/handler/ConditionHandler.java | 24 +++++---- .../internal/handler/HTTPHandler.java | 28 +++-------- .../internal/handler/NoiseHandler.java | 8 +-- .../internal/handler/PMHandler.java | 6 +-- .../internal/utils/Constants.java | 37 ++++++++++++++ .../resources/OH-INF/thing/thing-types.xml | 42 ++++++++++++---- .../internal/ConditionHandlerTest.java | 35 +++++++++++-- .../luftdateninfo/internal/DTOTest.java | 8 +-- .../internal/HTTPHandlerValueTest.java | 5 +- .../luftdateninfo/internal/PMHandlerTest.java | 27 +++++++++- .../src/test/resources/internal-data.json | 46 ++++++++++++++++++ 17 files changed, 256 insertions(+), 78 deletions(-) create mode 100644 bundles/org.openhab.binding.luftdateninfo/doc/local-sensor.png delete mode 100644 bundles/org.openhab.binding.luftdateninfo/doc/logo-rund.png create mode 100644 bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/utils/Constants.java create mode 100644 bundles/org.openhab.binding.luftdateninfo/src/test/resources/internal-data.json diff --git a/bundles/org.openhab.binding.luftdateninfo/README.md b/bundles/org.openhab.binding.luftdateninfo/README.md index 4b100712a..ff076ced1 100644 --- a/bundles/org.openhab.binding.luftdateninfo/README.md +++ b/bundles/org.openhab.binding.luftdateninfo/README.md @@ -1,7 +1,5 @@ # LuftdatenInfo Binding - - Binding for the Sensor Community [luftdaten.info](https://luftdaten.info/). The community provides instructions to build sensors on your own and they can be integrated into the database. With this binding you can integrate your sensor, a sensor nearby or even any sensors you want into openHAB. @@ -21,10 +19,21 @@ There's no auto discovery. See Thing configuration how to setup a Sensor. ## Thing Configuration +Choose either a local IP address of your personal owned sensor _or_ a sensor id of an external one. + | Parameter | Description | |-----------------|----------------------------------------------------------------------| +| ipAddress | Local IP address of your personal owned sensor | | sensorid | Sensor ID obtained from https://deutschland.maps.sensor.community/ | +### Local Sensor + +Please check in your browser if you can access your sensor with your local IP address. + +![Luftdaten.info Logo](doc/local-sensor.png) + +### External Sensor + Perform the following steps to get the appropriate Sensor ID * Go to to [luftdaten.info map](https://deutschland.maps.sensor.community/) @@ -69,9 +78,9 @@ Perform the following steps to get the appropriate Sensor ID luftdaten.things ``` -Thing luftdateninfo:particulate:pm_sensor "PM Sensor" [ sensorid=28842] -Thing luftdateninfo:conditions:cond_sensor "Condition Sensor" [ sensorid=28843] -Thing luftdateninfo:noise:noise_sensor "Noise Sensor" [ sensorid=39745] +Thing luftdateninfo:particulate:pm_sensor "PM Sensor" [ ipAddress=192.168.178.50 ] +Thing luftdateninfo:conditions:cond_sensor "Condition Sensor" [ sensorid=28843 ] +Thing luftdateninfo:noise:noise_sensor "Noise Sensor" [ sensorid=39745 ] ``` ### Items diff --git a/bundles/org.openhab.binding.luftdateninfo/doc/local-sensor.png b/bundles/org.openhab.binding.luftdateninfo/doc/local-sensor.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf4e6f1fd282e6765a692ccb238358ccf01e105 GIT binary patch literal 51060 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�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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.luftdateninfo/doc/logo-rund.png b/bundles/org.openhab.binding.luftdateninfo/doc/logo-rund.png deleted file mode 100644 index 962c7a9723c03d1f3a0bc4e679e64cb9b67273f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6398 zcmVTv?{EL8>Ul_-QMYEgXTW^V=`+(^RrmL+`>XrAzt_Db%yKN= za_2mCZWYK`h^{oEt5oGwpe^~#N&W&10{zL~L4`ixc~qVU-7ePd=v{lw;TgG+8X+^y zz|mN~ZR4q7$c2cm6_tw>T`|2Bq!08FQMS0iZ|Ylf{VUVk{?srl1hQ!Jt!Il5J}jc| z1T)|8Y7Soi>5I?TK4beMeGX5cuQiYg_d_5P{rLq=ox@&=V{((1yQ4>zVy9btU+gMj$)hv)V-T(EB zmFWNSYi>=|iPOVS?>*;x-%HneLCpdgvTa}fHTE;6g7XKu3x8Sb$u-R(i#Kg}55f;? zJ^u|H167sZ-M4Q2y(c>HgdNXb)u6d708l@HoX7!8$zD}2y1&bZTK&fpE3dF-v8~+mZ0(+Z+PO<3RU)J zO?cMJ7vGpW-th@XNY4XTUmP!5TgvXEf zz#U>%6W>_yh$#bXqV3ZhMCYq}8`YtGuU<|8j1ec<>bMD#Pv zgfM`>L0bqd8qyM#X)O=aRdRHcA?4!H^EyD_B6rjA4jeN=dNyrXD#D9E&E92w@Te$p zzZ#4RR+SWRZpVBsn75d-<}6@kdpE7=G=flajswL4zZ~e}r-%0Nu`%z}7OJRn$a zP!V23@(R86XC9rGKc+0lh}@)TgMuf0hbzuwZpq_}B-G*&_!>Zq`sjUQe;6ka zFMOyGR26U%e=MF$=dR#WZ@-8IsWes$Mhq4qAl9Kq;-Opp7>LzT@pa#eXiP^`5{EOHk3`W~Vnd5HAE^F6Vr3AACU}6hREEpp!&$RLB)8EE--g+75o4JUI8JRUxO%(k$ROb*i4Gpn(ltbR# zvx2}|rcerYLWmJEV#tUP7=sbPB4k8JiIC2uIInvFU%p^17o_K7)F4SPv-+7aNGyY- zcl37}NvqpfPMXljuG-9Fz6)Rxmf05mZ1pxhVSctF z4zW~1%KiO_I&?p;*p|FHCu*jyl@{Hf)T|FyEWi#~VkFd3G7mpSXQm{g3 zmXzEr8;x&MV{D64Q$$09zr z<_b=4?W83MJoG*@0GuN>Jf4-q5?=H z;1yLxn>f3aRf)Xg<^FztuaL1a8uVK5z#_6A7`9_r;nfl3g!r7*5n z6v2pKBE@)R5M^86%Y5sJZ9Fq@gy9rK#xdZPaycYOrSLLw`Yi30!-GR{b$(%x{O(>_ zmM_NJATCKt$WN^+xrc%_Wz<>CNO z_U&Zr?(IB%_y8pn;L?nw0*Y4o$^M;Ob;h}gP>)ZmEQUv3c|NJU_eie5(5_y(PFsQt zrps8>?E%7$X1mYTdmh#ZCyEdkTi1j!PoW}g_|;>4^V!F-QGl~LE`(lIPel|jLL^{} zrEKFERmOpFfCw9&ev}JVtYnd-aV9R*(}Yu_u@n2#eY^RWmwp8zalxN@WS~ev_tL(6 zDbB{l-cuVO>@awmvbkdwN|IewI6Ram6vbj{A0i4)V^=!_b`%a%^sqne$PqChjsbD> z8^@qkqD04M90VTPD?&CJ<;AP=Z^c9W*_I#BR~|+j5P7^tNaU$_}YHJ?r$3p!J0SEA`+r`~>bALYhbGB0k3w69N>?kzOh9D8;qmGa)sbq)vFb<=6v5;0`8?&FdoUVN zvB@k{qx$mbR;XHe;+rOraWF#JIX?c(L-Y-mz!}_e%wJD(V;&;$47-TWKXN~>3=fSv zFOHkV=l}ce9{%7RYnX#Qrb7aS71=ia^r9=7S4rYlNf}URLE5v}j#iwJx++lwMe{}S zuk?}%qq^o1Bk`ax07ltT`=!%|I4KwKVr7VrJ@zC1HGhD}LnOzEY$*&H&vS!0{$TSx z{P4ibI1?Uy3IeoOl&jwI4o)!!Q1qxNQbh>7a&FgL*7U3-U5xjy6oteHBWTHxYHvYJ z?GlO2W=Akkq_ndaajxb|t*dNDh&t^dk5isZ?whtWU@1FiCUwZg{emUGv;2^X*t}a)TS-sW6&F@^t+*BG7GrG29 zEMXCh7z)FK+_(J~kV<2$85714Y!EwrE_5T8D<>}0FbZ0!5Dt~dbhZ%&@tk6GO#R-N zCIp__v+jm3!q_#2%0B@&pKRxz495Bs)}!d-G+#e4~IEY8fMT~ zoHYbKUfyQ?_($K1Vx1wdLWd1N%yHF5+#v=LXw7C2Rl@!}>CSdEn~v9D34}(lZ7o=1 zhzdo-SNm}?T~t+biYXMy@9Cv=*h` zHl7_W@LVy+-g1SK7y<_wH+k`a0g4iFpqQf;=Vni?A1{VX$Adb|K#}2Hy`;j4)t7hrow57_kZeWwv-&7bAL7+&f?_CJi)%??=C)H##jBwr zQ7S1%#ZG)zH! z9^Su`K&z{{1nWGdfjm^k8w`;MrmY3r(H1XGP0Za%l~?kBsCXn_AisNW?9nso(Hj`o z=i?WB29>`DhU@Idh9I8sK{6>aUF}4Px~NrYylgA{$pinyFAp5VM2b2^D<1VqCG_0> z?Bjgp-g`J+q}wW#a|Nh`BYae=Qlu?|ZEwNV$&aW_w z)9qguk&o3#r(sZWaYvAZKuQ%#xdPTroVOT%GYV?SAiTSKA**{<&>3XeH=JY33s14V zZx2H@b|$SKZ9&J_P@yqoyE~BdXbeFCgW`rug!!SQ8uP?Ob(*ZRgaGYor*-)fyft6z zU3c9lCcXz?@%^_hA+Rseu&p)fM;y>ov=ma#6)?_^SZKDE0>j9`Kmn~paj>z54vb-l ztdQ+$M>82<%tWu?21`VPLm2NzR99_n3#PN9oL#!;jJ=otx8Cs-k3#QdH|$lot45BS z1_ex_zFZ*nrF{bnbK40s7F%Z&R=iTm6`>rCT^e0tRk-DG!n!rZN@tOsV%8G!E{9gakv>;}$OzKbg6V9Ft*^wl0s6gi zgaX;O?%F*lAE9o#hW7LDLky{@aYOF7oS4MbfF#5t z&9r6+Mm>5JSU+&{*LIz#-I}T!de>e1BUJvTc6OVEcq^tjg({eAnwE}s)Qsk{RVIIu zjDiFKsqPMZ%8u0a`V*M3aTqinDRDqDj02K>G|ii8Z6VXv3L<}f_%mOG&MUB6d9wwx*(N;Q8hE+Rb$ncb)Hi0*np%H z!L+nK+TUIHY_0cGvp}A?`s#{{xEyJ29alZ51_DhcO{TN0?f{?$2GiY!PuaKvr4AUc zNkB5fBz$<`CC&KX<;HJ?Cv2eFPCZ2TUv}+5D_jQBS0B60K{d|g449S-+0OR5a^j)_ zfh9Ai15MSp-`n;1WS=@W^`lpThD}du}C_h`E z#2$U7bJQfR)%udaV&=3HCF-k=CpTvt|M6sm0Vg>^s#v<|wl{k*4+4v)(oQ3w;zt+% zD@dtKDOa2{pK?qac*J{re-R(n@~T_i?~PnK@W)^K<;n6rC4oTo)vM+~kcMy3Q9pEK z-H77R;R@w^tiIx-lY8_dtwat#kVisC6z{>8B&NpUWvjC8;LTrsaVi{7Ss(xlw%ocf z>WA!XvIfY7XRcM2dB#G zw3NNSy7q#GKV>;t{|)sK9(VKoO0T{^XI-THqX zj?!;N_$sp~M{iV65xfyhOJ*b>ISE3L|E;Z+RgDw|GLg1r7wpnyf z1Sc-80!h@DaigoGjHiO|59-|)4zB;)3(erCX#%NY!IoPW2Ht)`;A22*v*{m=YR#7@ zxKf#NzKHQ!w&n-@@s>O1sgK^5ygy^c{Fk^wQmZ-h$NX^l z?x9ln-Tc*mbYv#@nK6Nk#o{e@u0-93P~Hoi-Avj7&jQ~E`S#vR);C2hoq(U&g@v2$ zS`dhPH==7%)&i$bF9qoXeMFQk9v;}Y?%HWLU8jOsCy=AD refreshJob; + private Optional sensorUrl = Optional.empty(); + private boolean firstUpdate = true; public enum ConfigStatus { - OK, + INTERNAL_SENSOR_OK, + EXTERNAL_SENSOR_OK, IS_NULL, SENSOR_IS_NULL, SENSOR_ID_NEGATIVE, @@ -88,6 +93,7 @@ public abstract class BaseSensorHandler extends BaseThingHandler { @Override public void initialize() { + firstUpdate = true; lifecycleStatus = LifecycleStatus.INITIALIZING; scheduler.execute(this::startUp); } @@ -95,7 +101,7 @@ public abstract class BaseSensorHandler extends BaseThingHandler { private void startUp() { config = getConfigAs(LuftdatenInfoConfiguration.class); configStatus = checkConfig(config); - if (configStatus == ConfigStatus.OK) { + if (configStatus == ConfigStatus.INTERNAL_SENSOR_OK || configStatus == ConfigStatus.EXTERNAL_SENSOR_OK) { // start getting values dataUpdate(); } else { @@ -135,10 +141,16 @@ public abstract class BaseSensorHandler extends BaseThingHandler { */ private ConfigStatus checkConfig(@Nullable LuftdatenInfoConfiguration c) { if (c != null) { - if (c.sensorid >= 0) { - return ConfigStatus.OK; + if (c.ipAddress != null && !Constants.EMPTY.equals(c.ipAddress)) { + sensorUrl = Optional.of("http://" + c.ipAddress + "/data.json"); + return ConfigStatus.INTERNAL_SENSOR_OK; } else { - return ConfigStatus.SENSOR_ID_NEGATIVE; + if (c.sensorid >= 0) { + sensorUrl = Optional.of("http://data.sensor.community/airrohr/v1/sensor/" + c.sensorid + "/"); + return ConfigStatus.EXTERNAL_SENSOR_OK; + } else { + return ConfigStatus.SENSOR_ID_NEGATIVE; + } } } else { return ConfigStatus.IS_NULL; @@ -150,11 +162,21 @@ public abstract class BaseSensorHandler extends BaseThingHandler { } protected void dataUpdate() { - HTTPHandler.getHandler().request(config.sensorid, this); + if (sensorUrl.isPresent()) { + HTTPHandler.getHandler().request(sensorUrl.get(), this); + } } public void onResponse(String data) { - lastUpdateStatus = updateChannels(data); + if (firstUpdate) { + logger.debug("{} delivers {}", sensorUrl.get(), data); + firstUpdate = false; + } + if (configStatus == ConfigStatus.INTERNAL_SENSOR_OK) { + lastUpdateStatus = updateChannels("[" + data + "]"); + } else { + lastUpdateStatus = updateChannels(data); + } statusUpdate(lastUpdateStatus, EMPTY); } diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/ConditionHandler.java b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/ConditionHandler.java index a80ec1f03..d3417f1a1 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/ConditionHandler.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/ConditionHandler.java @@ -13,7 +13,8 @@ package org.openhab.binding.luftdateninfo.internal.handler; import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*; -import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*; +import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*; +import static org.openhab.core.library.unit.MetricPrefix.HECTO; import java.util.List; @@ -38,11 +39,10 @@ import org.openhab.core.thing.Thing; */ @NonNullByDefault public class ConditionHandler extends BaseSensorHandler { - protected QuantityType temperatureCache = QuantityType.valueOf(-1, SIUnits.CELSIUS); protected QuantityType humidityCache = QuantityType.valueOf(-1, Units.PERCENT); - protected QuantityType pressureCache = QuantityType.valueOf(-1, SIUnits.PASCAL); - protected QuantityType pressureSeaCache = QuantityType.valueOf(-1, SIUnits.PASCAL); + protected QuantityType pressureCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)); + protected QuantityType pressureSeaCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)); public ConditionHandler(Thing thing) { super(thing); @@ -55,18 +55,22 @@ public class ConditionHandler extends BaseSensorHandler { if (valueList != null) { if (HTTPHandler.getHandler().isCondition(valueList)) { valueList.forEach(v -> { - if (v.getValueType().equals(TEMPERATURE)) { + if (v.getValueType().endsWith(TEMPERATURE)) { temperatureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), SIUnits.CELSIUS); updateState(TEMPERATURE_CHANNEL, temperatureCache); - } else if (v.getValueType().equals(HUMIDITY)) { + } else if (v.getValueType().endsWith(HUMIDITY)) { humidityCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.PERCENT); updateState(HUMIDITY_CHANNEL, humidityCache); - } else if (v.getValueType().equals(PRESSURE)) { - pressureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), SIUnits.PASCAL); + } else if (v.getValueType().endsWith(PRESSURE)) { + pressureCache = QuantityType.valueOf( + NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1), + HECTO(SIUnits.PASCAL)); updateState(PRESSURE_CHANNEL, pressureCache); - } else if (v.getValueType().equals(PRESSURE_SEALEVEL)) { - pressureSeaCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), SIUnits.PASCAL); + } else if (v.getValueType().endsWith(PRESSURE_SEALEVEL)) { + pressureSeaCache = QuantityType.valueOf( + NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1), + HECTO(SIUnits.PASCAL)); updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache); } }); diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/HTTPHandler.java b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/HTTPHandler.java index ec47aa212..050b69c3d 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/HTTPHandler.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/HTTPHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.luftdateninfo.internal.handler; +import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*; + import java.time.LocalDateTime; import java.util.List; import java.util.Objects; @@ -42,19 +44,6 @@ public class HTTPHandler { private static final Gson GSON = new Gson(); private static final HTTPHandler HTTP_HANDLER = new HTTPHandler(); - public static final String P1 = "P1"; - public static final String P2 = "P2"; - - public static final String TEMPERATURE = "temperature"; - public static final String HUMIDITY = "humidity"; - public static final String PRESSURE = "pressure"; - public static final String PRESSURE_SEALEVEL = "pressure_at_sealevel"; - - public static final String NOISE_EQ = "noise_LAeq"; - public static final String NOISE_MIN = "noise_LA_min"; - public static final String NOISE_MAX = "noise_LA_max"; - - private static String sensorUrl = "http://data.sensor.community/airrohr/v1/sensor/"; private static @Nullable HttpClient commonHttpClient; public static void init(HttpClient httpClient) { @@ -65,12 +54,11 @@ public class HTTPHandler { return HTTP_HANDLER; } - public synchronized void request(int sensorId, BaseSensorHandler callback) { + public synchronized void request(String url, BaseSensorHandler callback) { HttpClient localClient = commonHttpClient; if (localClient == null) { logger.warn("HTTP Client not initialized"); } else { - String url = sensorUrl + sensorId + "/"; Request req = localClient.newRequest(url); req.timeout(15, TimeUnit.SECONDS).send(new BufferingResponseListener() { @NonNullByDefault({}) @@ -142,7 +130,7 @@ public class HTTPHandler { if (valueList == null) { return false; } - return valueList.stream().map(v -> v.getValueType()).filter(t -> t.equals(P1) || t.equals(P2)).findAny() + return valueList.stream().map(v -> v.getValueType()).filter(t -> t.endsWith(P1) || t.endsWith(P2)).findAny() .isPresent(); } @@ -150,9 +138,8 @@ public class HTTPHandler { if (valueList == null) { return false; } - return valueList.stream().map(v -> v.getValueType()).filter( - t -> t.equals(TEMPERATURE) || t.equals(HUMIDITY) || t.equals(PRESSURE) || t.equals(PRESSURE_SEALEVEL)) - .findAny().isPresent(); + return valueList.stream().map(v -> v.getValueType()).filter(t -> t.equals(TEMPERATURE) || t.endsWith(HUMIDITY) + || t.endsWith(PRESSURE) || t.endsWith(PRESSURE_SEALEVEL)).findAny().isPresent(); } public boolean isNoise(@Nullable List valueList) { @@ -160,6 +147,7 @@ public class HTTPHandler { return false; } return valueList.stream().map(v -> v.getValueType()) - .filter(t -> t.equals(NOISE_EQ) || t.equals(NOISE_MAX) || t.equals(NOISE_MIN)).findAny().isPresent(); + .filter(t -> t.endsWith(NOISE_EQ) || t.endsWith(NOISE_MAX) || t.endsWith(NOISE_MIN)).findAny() + .isPresent(); } } diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/NoiseHandler.java b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/NoiseHandler.java index 130bc0b85..9a984bed8 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/NoiseHandler.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/NoiseHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.luftdateninfo.internal.handler; import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*; -import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*; +import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*; import java.util.List; @@ -50,13 +50,13 @@ public class NoiseHandler extends BaseSensorHandler { if (valueList != null) { if (HTTPHandler.getHandler().isNoise(valueList)) { valueList.forEach(v -> { - if (v.getValueType().equals(NOISE_EQ)) { + if (v.getValueType().endsWith(NOISE_EQ)) { noiseEQCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL); updateState(NOISE_EQ_CHANNEL, noiseEQCache); - } else if (v.getValueType().equals(NOISE_MIN)) { + } else if (v.getValueType().endsWith(NOISE_MIN)) { noiseMinCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL); updateState(NOISE_MIN_CHANNEL, noiseMinCache); - } else if (v.getValueType().equals(NOISE_MAX)) { + } else if (v.getValueType().endsWith(NOISE_MAX)) { noiseMaxCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL); updateState(NOISE_MAX_CHANNEL, noiseMaxCache); } diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/PMHandler.java b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/PMHandler.java index 55dcfb16e..7ddd4e43a 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/PMHandler.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/handler/PMHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.luftdateninfo.internal.handler; import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*; -import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*; +import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*; import java.util.List; @@ -49,11 +49,11 @@ public class PMHandler extends BaseSensorHandler { if (valueList != null) { if (HTTPHandler.getHandler().isParticulate(valueList)) { valueList.forEach(v -> { - if (v.getValueType().equals(P1)) { + if (v.getValueType().endsWith(P1)) { pm100Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.MICROGRAM_PER_CUBICMETRE); updateState(PM100_CHANNEL, pm100Cache); - } else if (v.getValueType().equals(P2)) { + } else if (v.getValueType().endsWith(P2)) { pm25Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.MICROGRAM_PER_CUBICMETRE); updateState(PM25_CHANNEL, pm25Cache); diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/utils/Constants.java b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/utils/Constants.java new file mode 100644 index 000000000..18b1e7754 --- /dev/null +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/java/org/openhab/binding/luftdateninfo/internal/utils/Constants.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.luftdateninfo.internal.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link Constants} Constants used in this binding + * + * @author Bernd Weymann - Initial contribution + */ +@NonNullByDefault +public class Constants { + public static final String EMPTY = ""; + public static final String P1 = "P1"; + public static final String P2 = "P2"; + + public static final String TEMPERATURE = "temperature"; + public static final String HUMIDITY = "humidity"; + public static final String PRESSURE = "pressure"; + public static final String PRESSURE_SEALEVEL = "pressure_at_sealevel"; + + public static final String NOISE_EQ = "noise_LAeq"; + public static final String NOISE_MIN = "noise_LA_min"; + public static final String NOISE_MAX = "noise_LA_max"; + public static final int UNDEF = -1; +} diff --git a/bundles/org.openhab.binding.luftdateninfo/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.luftdateninfo/src/main/resources/OH-INF/thing/thing-types.xml index abe42dc4b..001a2c1cb 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.luftdateninfo/src/main/resources/OH-INF/thing/thing-types.xml @@ -14,9 +14,14 @@ - - - Sensor ID + + network-address + + Local IP address of your personal owned sensor + + + + Sensor ID from https://deutschland.maps.sensor.community/ @@ -33,9 +38,14 @@ - - - Sensor ID + + network-address + + Local IP address of your personal owned sensor + + + + Sensor ID from https://deutschland.maps.sensor.community/ @@ -51,9 +61,14 @@ - - - Sensor ID + + network-address + + Local IP address of your personal owned sensor + + + + Sensor ID from https://deutschland.maps.sensor.community/ @@ -61,44 +76,53 @@ Number:Density + Number:Density + Number:Temperature Temperature from the selected Sensor ID + Number:Dimensionless Humidity from the selected Sensor ID + Number:Pressure Atmospheric Pressure from the selected Sensor ID + Number:Pressure Atmospheric Pressure at sea level from the selected Sensor ID + Number:Dimensionless Average noise level from the selected Sensor ID + Number:Dimensionless Minimum noise level (last 2.5 minutes) from the selected Sensor ID + Number:Dimensionless Maximum noise level (last 2.5 minutes) from the selected Sensor ID + diff --git a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/ConditionHandlerTest.java b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/ConditionHandlerTest.java index 97eb43a56..bf04225f2 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/ConditionHandlerTest.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/ConditionHandlerTest.java @@ -13,6 +13,7 @@ package org.openhab.binding.luftdateninfo.internal; import static org.junit.jupiter.api.Assertions.*; +import static org.openhab.core.library.unit.MetricPrefix.HECTO; import java.util.HashMap; @@ -49,9 +50,9 @@ public class ConditionHandlerTest { UpdateStatus result = condHandler.updateChannels(pmJson); assertEquals(UpdateStatus.OK, result, "Valid update"); assertEquals(QuantityType.valueOf(22.7, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature"); - assertEquals(QuantityType.valueOf(61.0, Units.PERCENT), condHandler.getHumidity(), "Humidity"); - assertEquals(QuantityType.valueOf(-1, SIUnits.PASCAL), condHandler.getPressure(), "Pressure"); - assertEquals(QuantityType.valueOf(-1, SIUnits.PASCAL), condHandler.getPressureSea(), "Pressure Sea"); + assertEquals(QuantityType.valueOf(61., Units.PERCENT), condHandler.getHumidity(), "Humidity"); + assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure"); + assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea"); } else { assertTrue(false); } @@ -73,8 +74,9 @@ public class ConditionHandlerTest { assertEquals(UpdateStatus.OK, result, "Valid update"); assertEquals(QuantityType.valueOf(21.5, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature"); assertEquals(QuantityType.valueOf(58.5, Units.PERCENT), condHandler.getHumidity(), "Humidity"); - assertEquals(QuantityType.valueOf(100200.0, SIUnits.PASCAL), condHandler.getPressure(), "Pressure"); - assertEquals(QuantityType.valueOf(101968.7, SIUnits.PASCAL), condHandler.getPressureSea(), "Pressure Sea"); + assertEquals(QuantityType.valueOf(1002.0, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure"); + assertEquals(QuantityType.valueOf(1019.7, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), + "Pressure Sea"); } else { assertTrue(false); } @@ -126,4 +128,27 @@ public class ConditionHandlerTest { UpdateStatus result = condHandler.updateChannels(null); assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update"); } + + @Test + public void testInternalUpdate() { + ThingMock t = new ThingMock(); + + HashMap properties = new HashMap(); + // String sensorid taken from thing-types.xml + properties.put("ipAddress", "192.168.178.1"); + t.setConfiguration(properties); + + ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t); + String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json"); + if (pmJson != null) { + UpdateStatus result = condHandler.updateChannels("[" + pmJson + "]"); + assertEquals(UpdateStatus.OK, result, "Valid update"); + assertEquals(QuantityType.valueOf(17.6, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature"); + assertEquals(QuantityType.valueOf(57.8, Units.PERCENT), condHandler.getHumidity(), "Humidity"); + assertEquals(QuantityType.valueOf(986.8, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure"); + assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea"); + } else { + assertTrue(false); + } + } } diff --git a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/DTOTest.java b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/DTOTest.java index 07ad76d65..93355b041 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/DTOTest.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/DTOTest.java @@ -20,8 +20,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.luftdateninfo.internal.dto.SensorData; import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue; -import org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler; import org.openhab.binding.luftdateninfo.internal.util.FileReader; +import org.openhab.binding.luftdateninfo.internal.utils.Constants; import com.google.gson.Gson; @@ -44,16 +44,16 @@ public class DTOTest { SensorData d = valueArray[0]; // Assure latest data is taken String dateStr = d.getTimeStamp(); - if (dateStr.equals("2020-06-09 06:38:08")) { + if ("2020-06-09 06:38:08".equals(dateStr)) { // take newer one d = valueArray[1]; } List sensorDataVaueList = d.getSensorDataValues(); assertNotNull(d); sensorDataVaueList.forEach(v -> { - if (v.getValueType().equals(HTTPHandler.TEMPERATURE)) { + if (Constants.TEMPERATURE.equals(v.getValueType())) { assertEquals("22.70", v.getValue(), "Temperature"); - } else if (v.getValueType().equals(HTTPHandler.HUMIDITY)) { + } else if (Constants.HUMIDITY.equals(v.getValueType())) { assertEquals("61.00", v.getValue(), "Humidity"); } }); diff --git a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/HTTPHandlerValueTest.java b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/HTTPHandlerValueTest.java index faaf90ef3..ac4192cb0 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/HTTPHandlerValueTest.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/HTTPHandlerValueTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue; import org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler; import org.openhab.binding.luftdateninfo.internal.util.FileReader; +import org.openhab.binding.luftdateninfo.internal.utils.Constants; /** * The {@link HTTPHandlerValueTest} test values decoding of HTTPHandler @@ -61,9 +62,9 @@ public class HTTPHandlerValueTest { } private void testSensorValue(SensorDataValue s) { - if (s.getValueType().equals(HTTPHandler.TEMPERATURE)) { + if (s.getValueType().equals(Constants.TEMPERATURE)) { assertEquals("22.70", s.getValue(), "Temperature resource 1"); - } else if (s.getValueType().equals(HTTPHandler.HUMIDITY)) { + } else if (s.getValueType().equals(Constants.HUMIDITY)) { assertEquals("61.00", s.getValue(), "Humidity resource 1"); } else { assertTrue(false); diff --git a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/PMHandlerTest.java b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/PMHandlerTest.java index 3923dff40..d30183b4e 100644 --- a/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/PMHandlerTest.java +++ b/bundles/org.openhab.binding.luftdateninfo/src/test/java/org/openhab/binding/luftdateninfo/internal/PMHandlerTest.java @@ -64,7 +64,7 @@ public class PMHandlerTest { * Test if config status is 0 = CONFIG_OK for valid configuration. Take real int for comparison instead of * BaseHandler constants - in case of change test needs to be adapted */ - assertEquals(ConfigStatus.OK, pmHandler.getConfigStatus(), "Handler Configuration status"); + assertEquals(ConfigStatus.EXTERNAL_SENSOR_OK, pmHandler.getConfigStatus(), "Handler Configuration status"); } @Test @@ -161,11 +161,34 @@ public class PMHandlerTest { HashMap properties = new HashMap(); // String sensorid taken from thing-types.xml - properties.put("sensorid", 12345); + properties.put("ipAdress", "192.168.178.1"); t.setConfiguration(properties); PMHandlerExtension pmHandler = new PMHandlerExtension(t); UpdateStatus result = pmHandler.updateChannels(null); assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update"); } + + @Test + public void testInternalPMSensor() { + ThingMock t = new ThingMock(); + + HashMap properties = new HashMap(); + // String sensorid taken from thing-types.xml + properties.put("sensorid", 12345); + t.setConfiguration(properties); + + PMHandlerExtension pmHandler = new PMHandlerExtension(t); + pmHandler.initialize(); + String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json"); + if (pmJson != null) { + UpdateStatus result = pmHandler.updateChannels("[" + pmJson + "]"); + assertEquals(UpdateStatus.OK, result, "Valid update"); + assertEquals(QuantityType.valueOf(4.3, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(), "PM25"); + assertEquals(QuantityType.valueOf(10.5, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(), + "PM100"); + } else { + assertTrue(false); + } + } } diff --git a/bundles/org.openhab.binding.luftdateninfo/src/test/resources/internal-data.json b/bundles/org.openhab.binding.luftdateninfo/src/test/resources/internal-data.json new file mode 100644 index 000000000..04d75b42d --- /dev/null +++ b/bundles/org.openhab.binding.luftdateninfo/src/test/resources/internal-data.json @@ -0,0 +1,46 @@ +{ + "software_version": "NRZ-2020-133", + "age": "112", + "sensordatavalues": [ + { + "value_type": "SDS_P1", + "value": "10.52" + }, + { + "value_type": "SDS_P2", + "value": "4.32" + }, + { + "value_type": "BME280_temperature", + "value": "17.59" + }, + { + "value_type": "BME280_pressure", + "value": "98680.28" + }, + { + "value_type": "BME280_humidity", + "value": "57.78" + }, + { + "value_type": "samples", + "value": "5070500" + }, + { + "value_type": "min_micro", + "value": "28" + }, + { + "value_type": "max_micro", + "value": "20091" + }, + { + "value_type": "interval", + "value": "145000" + }, + { + "value_type": "signal", + "value": "-81" + } + ] +} \ No newline at end of file