Search in sources :

Example 1 with ProvisionedMeshNode

use of org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode in project openremote by openremote.

the class BaseMeshNetwork method deleteNode.

/**
 * Deletes a mesh node from the list of provisioned nodes
 *
 * <p>
 * Note that deleting a node manually will not reset the node, but only be deleted from the stored list of provisioned nodes.
 * However you may still be able to connect to the same node, if it was not reset since the network may still exist. This
 * would be useful to in case if a node was physically reset and needs to be removed from the mesh network/db
 * </p>
 *
 * @param meshNode node to be deleted
 * @return true if deleted and false otherwise
 */
public synchronized boolean deleteNode(ProvisionedMeshNode meshNode) {
    // Let's go through the nodes and delete if a node exists
    boolean nodeDeleted = false;
    for (ProvisionedMeshNode node : nodes) {
        if (node.getUuid().equalsIgnoreCase(meshNode.getUuid())) {
            excludeNode(node);
            nodes.remove(node);
            notifyNodeDeleted(node);
            nodeDeleted = true;
            break;
        }
    }
    // We must also check if there is a provisioner based on the node we deleted
    if (nodeDeleted) {
        for (Provisioner provisioner : provisioners) {
            if (provisioner.getProvisionerUuid().equalsIgnoreCase(meshNode.getUuid())) {
                provisioners.remove(provisioner);
                notifyProvisionerDeleted(provisioner);
                break;
            }
        }
    }
    return nodeDeleted;
}
Also used : ProvisionedMeshNode(org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode)

Example 2 with ProvisionedMeshNode

use of org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode in project openremote by openremote.

the class MeshNetwork method nextAvailableUnicastAddress.

/**
 * Returns the next unicast address for a node based on the number of elements
 * and the range allocated to the provisioner.
 * P.S. When setting up a new network don't forget to assign an address to the provisioner.
 * When importing a network make sure to create a new provisioner with a different address
 * which is the recommended approach. However you can also use the same provisioner
 * with a different address.
 *
 * @param elementCount Element count
 * @param provisioner  provisioner
 * @return Allocated unicast address or -1 if none
 * @throws IllegalArgumentException if there is no allocated unicast range to the provisioner
 */
public synchronized int nextAvailableUnicastAddress(final int elementCount, final Provisioner provisioner) throws IllegalArgumentException {
    if (provisioner.getAllocatedUnicastRanges().isEmpty()) {
        throw new IllegalArgumentException("Please allocate a unicast address range to the provisioner");
    }
    // Populate all addresses that are currently in use
    final ArrayList<Integer> usedAddresses = new ArrayList<>();
    for (ProvisionedMeshNode node : nodes) {
        usedAddresses.addAll(node.getElements().keySet());
    }
    // Excluded addresses with the current IvIndex and current IvIndex - 1 must be considered as addresses in use.
    if (networkExclusions.get(ivIndex.getIvIndex()) != null)
        usedAddresses.addAll(networkExclusions.get(ivIndex.getIvIndex()));
    if (networkExclusions.get(ivIndex.getIvIndex() - 1) != null)
        usedAddresses.addAll(networkExclusions.get(ivIndex.getIvIndex() - 1));
    Collections.sort(usedAddresses);
    // Iterate through all nodes just once, while iterating over ranges.
    int index = 0;
    for (AllocatedUnicastRange range : provisioner.getAllocatedUnicastRanges()) {
        // Start from the beginning of the current range.
        int address = range.getLowAddress();
        // Iterate through nodes that weren't checked yet in essence the used addresses which include the excluded adresses
        for (int usedAddress : usedAddresses) {
            // Skip nodes with addresses below the range.
            if (address > usedAddress) {
                continue;
            }
            // If we found a space before the current node, return the address.
            if (usedAddress > (address + (elementCount - 1))) {
                return address;
            }
            // Else, move the address to the next available address.
            address = usedAddress + 1;
            // If the new address is outside of the range, go to the next one.
            if (range.highAddress < address + (elementCount - 1)) {
                break;
            }
        }
        if (range.getHighAddress() >= address + (elementCount - 1)) {
            return address;
        }
    }
    // No address was found :(
    return -1;
}
Also used : ProvisionedMeshNode(org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode) ArrayList(java.util.ArrayList)

Example 3 with ProvisionedMeshNode

use of org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode in project openremote by openremote.

the class MeshProvisioningHandler method parseProvisioningState.

private void parseProvisioningState(final UnprovisionedMeshNode unprovisionedMeshNode, final byte[] data) {
    isProvisioningPublicKeySent = false;
    isProvisioneePublicKeyReceived = false;
    if (data[1] == ProvisioningState.State.PROVISIONING_COMPLETE.getState()) {
        provisioningState = new ProvisioningCompleteState(unprovisionedMeshNode);
        // Generate the network id and store it in the mesh node, this is needed to reconnect to the device at a later stage.
        final ProvisionedMeshNode provisionedMeshNode = new ProvisionedMeshNode(unprovisionedMeshNode);
        mInternalMeshManagerCallbacks.onNodeProvisioned(provisionedMeshNode);
        mStatusCallbacks.onProvisioningCompleted(provisionedMeshNode, ProvisioningState.States.PROVISIONING_COMPLETE, data);
    } else {
        final ProvisioningFailedState provisioningFailedState = new ProvisioningFailedState();
        provisioningState = provisioningFailedState;
        if (provisioningFailedState.parseData(data)) {
            mStatusCallbacks.onProvisioningFailed(unprovisionedMeshNode, ProvisioningState.States.PROVISIONING_FAILED, data);
        }
    }
}
Also used : ProvisionedMeshNode(org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode) ProvisioningFailedState(org.openremote.agent.protocol.bluetooth.mesh.provisionerstates.ProvisioningFailedState) ProvisioningCompleteState(org.openremote.agent.protocol.bluetooth.mesh.provisionerstates.ProvisioningCompleteState)

Example 4 with ProvisionedMeshNode

use of org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode in project openremote by openremote.

the class MeshManagerApi method parseNotifications.

/**
 * Parses notifications received by the client.
 *
 * @param unsegmentedPdu pdu received by the client.
 */
private void parseNotifications(final byte[] unsegmentedPdu) {
    try {
        switch(unsegmentedPdu[0]) {
            case PDU_TYPE_NETWORK:
                // MeshNetwork PDU
                LOG.info("Received network pdu: " + MeshParserUtils.bytesToHex(unsegmentedPdu, true));
                mMeshMessageHandler.parseMeshPduNotifications(unsegmentedPdu, mMeshNetwork);
                break;
            case PDU_TYPE_MESH_BEACON:
                // Validate SNBs against all network keys
                NetworkKey networkKey;
                for (int i = 0; i < mMeshNetwork.getNetKeys().size(); i++) {
                    networkKey = mMeshNetwork.getNetKeys().get(i);
                    final byte[] receivedBeaconData = new byte[unsegmentedPdu.length - 1];
                    System.arraycopy(unsegmentedPdu, 1, receivedBeaconData, 0, receivedBeaconData.length);
                    final SecureNetworkBeacon receivedBeacon = new SecureNetworkBeacon(receivedBeaconData);
                    final byte[] n = networkKey.getTxNetworkKey();
                    final int flags = receivedBeacon.getFlags();
                    final byte[] networkId = SecureUtils.calculateK3(n);
                    final int ivIndex = receivedBeacon.getIvIndex().getIvIndex();
                    LOG.info("Received mesh beacon: " + receivedBeacon.toString());
                    final SecureNetworkBeacon localSecureNetworkBeacon = SecureUtils.createSecureNetworkBeacon(n, flags, networkId, ivIndex);
                    // Check the the beacon received is a valid by matching the authentication values
                    if (Arrays.equals(receivedBeacon.getAuthenticationValue(), localSecureNetworkBeacon.getAuthenticationValue())) {
                        LOG.info("Secure Network Beacon beacon authenticated.");
                        // beacon on a secondary subnet, it will disregard it.
                        if (mMeshNetwork.getPrimaryNetworkKey() != null && networkKey.keyIndex != 0) {
                            LOG.info("Discarding beacon for secondary subnet with network key index: " + networkKey.keyIndex);
                            return;
                        }
                        // Get the last IV Index.
                        // / The last used IV Index for this mesh network.
                        final IvIndex lastIvIndex = mMeshNetwork.getIvIndex();
                        LOG.info("Last IV Index: " + lastIvIndex.getIvIndex());
                        // / The date of the last change of IV Index or IV Update Flag.
                        final Calendar lastTransitionDate = lastIvIndex.getTransitionDate();
                        // / A flag whether the IV has recently been updated using IV Recovery procedure.
                        // / The at-least-96h requirement for the duration of the current state will not apply.
                        // / The node shall not execute more than one IV Index Recovery within a period of 192 hours.
                        final boolean isIvRecoveryActive = lastIvIndex.getIvRecoveryFlag();
                        // / The test mode disables the 96h rule, leaving all other behavior unchanged.
                        final boolean isIvTestModeActive = ivUpdateTestModeActive;
                        final boolean flag = allowIvIndexRecoveryOver42;
                        if (!receivedBeacon.canOverwrite(lastIvIndex, lastTransitionDate, isIvRecoveryActive, isIvTestModeActive, flag)) {
                            String numberOfHoursSinceDate = ((Calendar.getInstance().getTimeInMillis() - lastTransitionDate.getTimeInMillis()) / (3600 * 1000)) + "h";
                            LOG.info("Discarding beacon " + receivedBeacon.getIvIndex() + ", last " + lastIvIndex.getIvIndex() + ", changed: " + numberOfHoursSinceDate + "ago, test mode: " + ivUpdateTestModeActive);
                            return;
                        }
                        final IvIndex receivedIvIndex = receivedBeacon.getIvIndex();
                        mMeshNetwork.ivIndex = new IvIndex(receivedIvIndex.getIvIndex(), receivedIvIndex.isIvUpdateActive(), lastTransitionDate);
                        if (mMeshNetwork.ivIndex.getIvIndex() > lastIvIndex.getIvIndex()) {
                            LOG.info("Applying: " + mMeshNetwork.ivIndex.getIvIndex());
                        }
                        // the Node shall reset the sequence number to 0x000000.
                        if (mMeshNetwork.ivIndex.getTransmitIvIndex() > lastIvIndex.getTransmitIvIndex()) {
                            LOG.info("Resetting local sequence numbers to 0");
                            final Provisioner provisioner = mMeshNetwork.getSelectedProvisioner();
                            final ProvisionedMeshNode node = mMeshNetwork.getNode(provisioner.getProvisionerUuid());
                            node.setSequenceNumber(0);
                        }
                        // Updating the iv recovery flag
                        if (lastIvIndex != mMeshNetwork.ivIndex) {
                            final boolean ivRecovery = mMeshNetwork.getIvIndex().getIvIndex() > lastIvIndex.getIvIndex() + 1 && !receivedBeacon.getIvIndex().isIvUpdateActive();
                            mMeshNetwork.getIvIndex().setIvRecoveryFlag(ivRecovery);
                        }
                        if (!mMeshNetwork.ivIndex.getIvRecoveryFlag()) {
                            final Iterator<Map.Entry<Integer, ArrayList<Integer>>> iterator = mMeshNetwork.networkExclusions.entrySet().iterator();
                            while (iterator.hasNext()) {
                                final Map.Entry<Integer, ArrayList<Integer>> exclusions = iterator.next();
                                final int expectedIncrement = exclusions.getKey() + 2;
                                if (mMeshNetwork.ivIndex.getIvIndex() >= expectedIncrement) {
                                    // Clear the last known sequence number of addresses that are to be removed from the exclusion list.
                                    // Decided to retain the last known sequence number as the IV Indexes increment the sequence number
                                    // will be greater than the last known anyways
                                    // for (Integer address : mMeshNetwork.networkExclusions.get(expectedIncrement)) {
                                    // mMeshNetwork.sequenceNumbers.removeAt(address);
                                    // }
                                    iterator.remove();
                                }
                            }
                        }
                    }
                }
                break;
            case PDU_TYPE_PROXY_CONFIGURATION:
                // Proxy configuration
                LOG.info("Received proxy configuration message: " + MeshParserUtils.bytesToHex(unsegmentedPdu, true));
                mMeshMessageHandler.parseMeshPduNotifications(unsegmentedPdu, mMeshNetwork);
                break;
            case PDU_TYPE_PROVISIONING:
                // Provisioning PDU
                LOG.info("Received provisioning message: " + MeshParserUtils.bytesToHex(unsegmentedPdu, true));
                mMeshProvisioningHandler.parseProvisioningNotifications(unsegmentedPdu);
                break;
        }
    } catch (ExtendedInvalidCipherTextException ex) {
    // TODO handle decryption failure
    } catch (IllegalArgumentException ex) {
        LOG.severe("Parsing notification failed: " + MeshParserUtils.bytesToHex(unsegmentedPdu, true) + " - " + ex.getMessage());
    }
}
Also used : ProvisionedMeshNode(org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode) Calendar(java.util.Calendar) ArrayList(java.util.ArrayList) ExtendedInvalidCipherTextException(org.openremote.agent.protocol.bluetooth.mesh.utils.ExtendedInvalidCipherTextException) Map(java.util.Map)

Example 5 with ProvisionedMeshNode

use of org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode in project openremote by openremote.

the class MeshManagerApi method generateMeshNetwork.

private MeshNetwork generateMeshNetwork(int provisionerAddress) {
    final String meshUuid = UUID.randomUUID().toString().toUpperCase(Locale.US);
    final MeshNetwork network = new MeshNetwork(meshUuid);
    // network.netKeys = generateNetKeys(meshUuid);
    // network.appKeys = generateAppKeys(meshUuid);
    // final AllocatedUnicastRange unicastRange = new AllocatedUnicastRange(0x0001, 0x199A);
    final AllocatedUnicastRange unicastRange = new AllocatedUnicastRange(provisionerAddress, provisionerAddress);
    final AllocatedGroupRange groupRange = new AllocatedGroupRange(0xC000, 0xCC9A);
    final AllocatedSceneRange sceneRange = new AllocatedSceneRange(0x0001, 0x3333);
    final Provisioner provisioner = network.createProvisioner("nRF Mesh Provisioner", unicastRange, groupRange, sceneRange);
    final int unicast = provisioner.getAllocatedUnicastRanges().get(0).getLowAddress();
    provisioner.assignProvisionerAddress(unicast);
    network.selectProvisioner(provisioner);
    network.addProvisioner(provisioner);
    final ProvisionedMeshNode node = network.getNode(unicast);
    if (node != null) {
        network.unicastAddress = node.getUnicastAddress() + (node.getNumberOfElements() - 1);
    } else {
        network.unicastAddress = 1;
    }
    network.lastSelected = true;
    // Clear the sequence numbers first
    network.sequenceNumbers.clear();
    network.loadSequenceNumbers();
    ivUpdateTestModeActive = false;
    allowIvIndexRecoveryOver42 = false;
    return network;
}
Also used : ProvisionedMeshNode(org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode)

Aggregations

ProvisionedMeshNode (org.openremote.agent.protocol.bluetooth.mesh.transport.ProvisionedMeshNode)9 ArrayList (java.util.ArrayList)3 Calendar (java.util.Calendar)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 ProvisioningCompleteState (org.openremote.agent.protocol.bluetooth.mesh.provisionerstates.ProvisioningCompleteState)1 ProvisioningFailedState (org.openremote.agent.protocol.bluetooth.mesh.provisionerstates.ProvisioningFailedState)1 Element (org.openremote.agent.protocol.bluetooth.mesh.transport.Element)1 MeshModel (org.openremote.agent.protocol.bluetooth.mesh.transport.MeshModel)1 ExtendedInvalidCipherTextException (org.openremote.agent.protocol.bluetooth.mesh.utils.ExtendedInvalidCipherTextException)1