use of me.retrodaredevil.solarthing.packets.Packet in project solarthing by wildmountainfarms.
the class PVOutputHandler method getStatus.
public AddStatusParameters getStatus(long dayStartTimeMillis, List<FragmentedPacketGroup> packetGroupList) {
FragmentedPacketGroup latestPacketGroup = packetGroupList.get(packetGroupList.size() - 1);
LOGGER.debug("Continuing with the latest packet group. Day start: " + dayStartTimeMillis);
AddStatusParametersBuilder addStatusParametersBuilder = createStatusBuilder(zoneId, latestPacketGroup.getDateMillis());
setStatusPowerValues(addStatusParametersBuilder, latestPacketGroup);
setStatusEnergyValues(addStatusParametersBuilder, packetGroupList, AccumulationConfig.createDefault(dayStartTimeMillis));
for (Packet packet : latestPacketGroup.getPackets()) {
if (packet instanceof PVCurrentAndVoltage) {
int fragmentId = latestPacketGroup.getFragmentId(packet);
PVCurrentAndVoltage pvCurrentAndVoltage = (PVCurrentAndVoltage) packet;
IdentifierFragment identifierFragment = IdentifierFragment.create(fragmentId, pvCurrentAndVoltage.getIdentifier());
if (voltageIdentifierFragmentMatcher.matches(identifierFragment)) {
float voltage = pvCurrentAndVoltage.getPVVoltage().floatValue();
addStatusParametersBuilder.setVoltage(voltage);
}
} else if (packet instanceof TemperaturePacket) {
int fragmentId = latestPacketGroup.getFragmentId(packet);
TemperaturePacket temperaturePacket = (TemperaturePacket) packet;
IdentifierFragment identifierFragment = IdentifierFragment.create(fragmentId, temperaturePacket.getIdentifier());
if (temperatureIdentifierFragmentMatcher.matches(identifierFragment)) {
float temperatureCelsius = temperaturePacket.getTemperatureCelsius();
if (!TemperaturePacket.POSSIBLE_BAD_VALUES.contains(temperatureCelsius)) {
addStatusParametersBuilder.setTemperatureCelsius(temperatureCelsius);
} else {
LOGGER.info("Not setting temperature: " + temperatureCelsius + " because it could be a bad reading");
}
}
}
}
return addStatusParametersBuilder.build();
}
use of me.retrodaredevil.solarthing.packets.Packet in project solarthing by wildmountainfarms.
the class RequireFullOutputActionNode method isFullOutput.
private boolean isFullOutput(FragmentedPacketGroup latestPacketGroup) {
for (Map.Entry<Integer, List<String>> entry : requiredIdentifierMap.entrySet()) {
int desiredFragmentId = entry.getKey();
if (!latestPacketGroup.hasFragmentId(desiredFragmentId)) {
continue;
}
for (String desiredIdentifierRepresentation : entry.getValue()) {
for (Packet packet : latestPacketGroup.getPackets()) {
int fragmentId = latestPacketGroup.getFragmentId(packet);
if (fragmentId != desiredFragmentId) {
continue;
}
if (packet instanceof Identifiable) {
Identifier identifier = ((Identifiable) packet).getIdentifier();
if (desiredIdentifierRepresentation.equals(identifier.getRepresentation())) {
if (packet instanceof MXStatusPacket) {
MXStatusPacket mx = (MXStatusPacket) packet;
ChargerMode mode = mx.getChargingMode();
// Some old MX firmwares consistently report being in float mode after a full absorb cycle even after the daily reset.
// This configuration option allows us to treat float as full output for a given MX
boolean canIgnoreFloatMode = ignoreReportedMXFloatMap.getOrDefault(fragmentId, Collections.emptyList()).contains(desiredIdentifierRepresentation);
if (mode != ChargerMode.SILENT && mode != ChargerMode.BULK && (!canIgnoreFloatMode || mode != ChargerMode.FLOAT)) {
if (log) {
LOGGER.info(identifier.getRepresentation() + " on fragment " + fragmentId + " is in mode " + mode.getModeName());
}
return false;
}
} else if (packet instanceof RoverStatusPacket) {
RoverStatusPacket rover = (RoverStatusPacket) packet;
ChargingState state = rover.getChargingMode();
if (state != ChargingState.ACTIVATED && state != ChargingState.DEACTIVATED && state != ChargingState.MPPT) {
if (log) {
LOGGER.info(identifier.getRepresentation() + " on fragment " + fragmentId + " is in mode " + state.getModeName());
}
return false;
}
} else if (packet instanceof TracerStatusPacket) {
TracerStatusPacket tracer = (TracerStatusPacket) packet;
ChargingStatus status = tracer.getChargingMode();
// we currently cannot tell if while in BOOST or EQUALIZE if the controller is actually in Bulk, so float is the only mode we know that isn't at full output
if (status == ChargingStatus.FLOAT) {
if (log) {
LOGGER.info(identifier.getRepresentation() + " on fragment " + fragmentId + " is in mode " + status.getModeName());
}
return false;
}
}
}
}
}
}
}
return true;
}
use of me.retrodaredevil.solarthing.packets.Packet in project solarthing by wildmountainfarms.
the class TracerEventUpdaterListReceiver method useData.
private void useData(@NotNull TracerStatusPacket tracer, @Nullable TracerStatusPacket previous) {
final Integer lastChargingEquipmentStatusValue;
if (previous == null) {
lastChargingEquipmentStatusValue = null;
} else {
lastChargingEquipmentStatusValue = previous.getChargingEquipmentStatus();
}
int currentChargingEquipmentStatusValue = tracer.getChargingEquipmentStatus();
List<Packet> packets = new ArrayList<>();
if (lastChargingEquipmentStatusValue == null || lastChargingEquipmentStatusValue != currentChargingEquipmentStatusValue) {
packets.add(new ImmutableTracerChargingEquipmentStatusChangePacket(tracer.getIdentifier(), currentChargingEquipmentStatusValue, lastChargingEquipmentStatusValue));
}
if (!packets.isEmpty()) {
eventReceiver.receive(packets);
}
}
use of me.retrodaredevil.solarthing.packets.Packet in project solarthing by wildmountainfarms.
the class TracerEventUpdaterListReceiver method receive.
@Override
public void receive(List<Packet> packets) {
for (Packet packet : packets) {
if (packet instanceof TracerStatusPacket) {
TracerStatusPacket tracer = (TracerStatusPacket) packet;
TracerStatusPacket previous = previousPacketMap.get(tracer.getIdentifier());
previousPacketMap.put(tracer.getIdentifier(), tracer);
useData(tracer, previous);
}
}
}
use of me.retrodaredevil.solarthing.packets.Packet in project solarthing by wildmountainfarms.
the class SecurityPacketReceiver method receivePacketGroup.
private void receivePacketGroup(StoredPacketGroup storedPacketGroup, TargetPacketGroup packetGroup) {
LOGGER.debug("Receiving packet group: " + storedPacketGroup.getStoredIdentifier());
long packetGroupDateMillis = packetGroup.getDateMillis();
List<? extends Packet> packetGroupPackets = packetGroup.getPackets();
if (packetGroupPackets.stream().noneMatch(packet -> packet instanceof SecurityPacket && !(packet instanceof AuthNewSenderPacket))) {
LOGGER.debug("This packet group has no useful SecurityPackets. Ignoring. identifier: " + storedPacketGroup.getStoredIdentifier());
return;
}
if (packetGroupPackets.size() != 1) {
/*
In any "millis database" we have the documents inside follow the packet collection format.
Packet collections can have many packets inside them. When we are dealing with security packets,
we want to be able to easily refer to a single security packet. Because no one should ever need
to contain multiple security packets in a given packet collection, we will only process
packet collections with a single security packet. This allows things like SecurityEventPackets to work nicely,
and doesn't have any real drawbacks except for some (possible) future use case that we don't know of.
*/
LOGGER.warn("This packetGroup targeting us had a packetGroup.size != 1! stored identifier: " + storedPacketGroup.getStoredIdentifier());
reject(storedPacketGroup, SecurityRejectPacket.Reason.INVALID_DATA, "You cannot have more than one packet (besides source and target)");
return;
}
Packet packet = packetGroupPackets.stream().findFirst().orElseThrow(() -> new AssertionError("size should be 1! This should not fail"));
if (!(packet instanceof LargeIntegrityPacket)) {
if (packet instanceof IntegrityPacket) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "Got an IntegrityPacket! This is no longer supported!");
reject(storedPacketGroup, SecurityRejectPacket.Reason.LEGACY_REQUEST, "IntegrityPacket is no longer supported");
} else if (packet instanceof AuthNewSenderPacket) {
LOGGER.warn("Got an AuthNewSenderPacket. Ignoring.");
reject(storedPacketGroup, SecurityRejectPacket.Reason.UNKNOWN_ERROR, "Got auth new sender packet");
} else {
LOGGER.warn("Unknown packet: " + packet);
reject(storedPacketGroup, SecurityRejectPacket.Reason.UNKNOWN_ERROR, "Got unknown packet");
}
return;
}
LargeIntegrityPacket largeIntegrityPacket = (LargeIntegrityPacket) packet;
String sender = largeIntegrityPacket.getSender();
final String invalidSenderReason = sender == null ? "sender is null!" : SenderUtil.getInvalidSenderNameReason(sender);
if (invalidSenderReason != null) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, invalidSenderReason);
reject(storedPacketGroup, SecurityRejectPacket.Reason.INVALID_DATA, invalidSenderReason);
return;
}
String encodedHash = decryptData(storedPacketGroup, sender, largeIntegrityPacket.getEncryptedHash(), packetGroupDateMillis);
if (encodedHash == null) {
// If encodedHash is null, then decryptData should have already called reject()
return;
}
byte[] decodedHash;
try {
decodedHash = Base64Variants.getDefaultVariant().decode(encodedHash);
} catch (IllegalArgumentException e) {
LOGGER.error("Not base64 data!", e);
reject(storedPacketGroup, SecurityRejectPacket.Reason.INVALID_DATA, "Not base64 data!");
return;
}
String payload = largeIntegrityPacket.getPayload();
byte[] payloadHash = HashUtil.hash(payload);
if (!Arrays.equals(decodedHash, payloadHash)) {
LOGGER.warn("Unsuccessfully compared hashes! The data may have been tampered with!");
reject(storedPacketGroup, SecurityRejectPacket.Reason.DIFFERENT_PAYLOAD, "Unsuccessfully compared hashes");
return;
}
LOGGER.debug("Successfully compared hashes!");
handleMessage(storedPacketGroup, payload, sender);
}
Aggregations