diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/AlsoEnergyCloudDatumStreamService.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/AlsoEnergyCloudDatumStreamService.java index 6213ba242..59a9053d9 100644 --- a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/AlsoEnergyCloudDatumStreamService.java +++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/AlsoEnergyCloudDatumStreamService.java @@ -23,12 +23,15 @@ package net.solarnetwork.central.c2c.biz.impl; import static net.solarnetwork.central.c2c.biz.impl.BaseCloudIntegrationService.resolveBaseUrl; +import static net.solarnetwork.central.c2c.domain.CloudDataValue.DEVICE_SERIAL_NUMBER_METADATA; +import static net.solarnetwork.central.c2c.domain.CloudDataValue.dataValue; import static net.solarnetwork.central.c2c.domain.CloudDataValue.intermediateDataValue; import static net.solarnetwork.central.security.AuthorizationException.requireNonNullObject; import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument; import static org.springframework.web.util.UriComponentsBuilder.fromUri; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -72,6 +75,15 @@ public class AlsoEnergyCloudDatumStreamService extends BaseOAuth2ClientCloudDatu /** The data value filter key for a site ID. */ public static final String SITE_ID_FILTER = "siteId"; + /** + * The URI path to list the hardware for a given site. + * + *

+ * Accepts a single {@code {siteId}} parameter. + *

+ */ + public static final String SITE_HARDWARE_URL_TEMPLATE = "/sites/{siteId}/hardware"; + /** The service settings. */ public static final List SETTINGS; static { @@ -156,6 +168,8 @@ public Iterable dataValues(UserLongCompositePK integrationId, List result = Collections.emptyList(); if ( false ) { // TODO + } else if ( filters != null && filters.get(SITE_ID_FILTER) != null ) { + result = siteHardware(integration, filters); } else { // list available sites result = sites(integration); @@ -185,6 +199,15 @@ private List sites(CloudIntegrationConfiguration integration) { res -> parseSites(res.getBody())); } + private List siteHardware(CloudIntegrationConfiguration integration, + Map filters) { + return restOpsHelper.httpGet("List sites", integration, JsonNode.class, + (req) -> fromUri(resolveBaseUrl(integration, AlsoEnergyCloudIntegrationService.BASE_URI)) + .path(SITE_HARDWARE_URL_TEMPLATE).queryParam("includeArchivedFields", true) + .buildAndExpand(filters).toUri(), + res -> parseSiteHardware(res.getBody(), filters)); + } + private static List parseSites(JsonNode json) { assert json != null; /*- EXAMPLE JSON: @@ -208,4 +231,92 @@ private static List parseSites(JsonNode json) { return result; } + private static List parseSiteHardware(JsonNode json, Map filters) { + assert json != null; + /*- EXAMPLE JSON: + { + "hardware": [ + { + "id": 12345, + "stringId": "C12345_S12345_PM0", + "functionCode": "PM", + "flags": [ + "IsEnabled" + ], + "fieldsArchived": [ + "KWHnet", + "KW", + "KVAR", + "PowerFactor", + "KWHrec", + "KWHdel", + "Frequency", + "VacA", + "VacB", + "VacC", + "VacAB", + "VacBC", + "VacCA", + "IacA", + "IacB", + "IacC" + ], + "name": "Elkor Production Meter - A", + "lastUpdate": "2024-11-21T17:19:02.4069164-05:00", + "lastUpload": "2024-11-21T17:17:00-05:00", + "iconUrl": "https://alsoenergy.com/Pub/Images/device/7855.png", + "config": { + "deviceType": "ProductionPowerMeter", + "hardwareStrId": "C12345_S12345_PM0", + "hardwareId": 12345, + "address": 1, + "portNumber": 1, + "baudRate": 9600, + "comType": "Rs485_2Wire", + "serialNumber": "12345", + "name": "Elkor Production Meter - A", + "outputHardwareId": 0, + "weatherStationId": 0, + "meterConfig": { + "scaleFactor": 0.0, + "isReversed": false, + "grossEnergy": "None", + "maxPowerKw": 58444.96, + "maxVoltage": 480.0, + "maxAmperage": 12200.0, + "acPhase": "Wye" + } + } + }, + */ + final String siteId = filters.get(SITE_ID_FILTER).toString(); + final var result = new ArrayList(4); + for ( JsonNode meterNode : json.path("hardware") ) { + final JsonNode fieldsNode = meterNode.path("fieldsArchived"); + if ( !(fieldsNode.isArray() && fieldsNode.size() > 0) ) { + continue; + } + final String id = meterNode.path("id").asText().trim(); + if ( id.isEmpty() ) { + continue; + } + final String name = meterNode.path("name").asText().trim(); + final var meta = new LinkedHashMap(4); + populateNonEmptyValue(meterNode, "functionCode", "functionCode", meta); + for ( JsonNode configNode : meterNode.path("config") ) { + populateNonEmptyValue(configNode, "serialNumber", DEVICE_SERIAL_NUMBER_METADATA, meta); + populateNonEmptyValue(configNode, "deviceType", "deviceType", meta); + } + + List fields = new ArrayList<>(fieldsNode.size()); + for ( JsonNode fieldNode : fieldsNode ) { + final String fieldName = fieldNode.asText(); + fields.add(dataValue(List.of(siteId, id, fieldName), fieldName)); + } + + result.add(intermediateDataValue(List.of(siteId, id), name, meta, fields)); + } + return result; + } + }