def parseErrorState(self, meshErrorState): self.crownstoneId = meshErrorState[0] self.hasError = True self.errorMode = True self.errorsBitmask = Conversion.uint8_array_to_uint32([ meshErrorState[1], meshErrorState[2], meshErrorState[3], meshErrorState[4] ]) self.errorTimestamp = Conversion.uint8_array_to_uint32([ meshErrorState[5], meshErrorState[6], meshErrorState[7], meshErrorState[8] ]) self.flagsBitmask = meshErrorState[9] self.temperature = meshErrorState[10] self.partialTimestamp = Conversion.uint8_array_to_uint16(meshErrorState[11:13]) self.timestamp = reconstructTimestamp(time.time(), self.partialTimestamp) if self.crownstoneId == 0: self.deprecated = True
def _verifyDecryption(decrypted, validationKey): # the conversion to uint32 only takes the first 4 bytes if Conversion.uint8_array_to_uint32(decrypted) == Conversion.uint8_array_to_uint32(validationKey): # remove checksum from decryption and return payload result = [0] * (len(decrypted) - SESSION_KEY_LENGTH) for i in range(0,len(result)): result[i] = decrypted[i+SESSION_KEY_LENGTH] return result else: raise BluenetBleException(BleError.ENCRYPTION_VALIDATION_FAILED, "Failed to validate result, Could not decrypt")
def parseOpCode3_type1(serviceData, data): if len(data) == 17: # opCode = data[0] # dataType = data[1] serviceData.errorMode = True serviceData.crownstoneId = data[2] serviceData.errorsBitmask = Conversion.uint8_array_to_uint32([ data[3], data[4], data[5], data[6] ]) serviceData.errorTimestamp = Conversion.uint8_array_to_uint32([ data[7], data[8], data[9], data[10] ]) serviceData.flagsBitmask = data[11] # bitmask states bitmaskArray = Conversion.uint8_to_bit_array(serviceData.flagsBitmask) serviceData.dimmingAvailable = bitmaskArray[0] serviceData.dimmingAllowed = bitmaskArray[1] serviceData.hasError = bitmaskArray[2] serviceData.switchLocked = bitmaskArray[3] serviceData.timeIsSet = bitmaskArray[4] serviceData.switchCraftEnabled = bitmaskArray[5] serviceData.temperature = Conversion.uint8_to_int8(data[12]) serviceData.partialTimestamp = Conversion.uint8_array_to_uint16([data[13], data[14]]) serviceData.uniqueIdentifier = serviceData.partialTimestamp if serviceData.timeIsSet: serviceData.timestamp = reconstructTimestamp(time.time(), serviceData.partialTimestamp) else: serviceData.timestamp = serviceData.partialTimestamp # this is now a counter realPower = Conversion.uint16_to_int16( Conversion.uint8_array_to_uint16([ data[15], data[16] ]) ) serviceData.powerUsageReal = float(realPower) / 8.0 # this packet has no validation serviceData.validation = 0
def __init__(self, payload): self.stoneStates = [] if len(payload) != MESH_STATE_PACKET_SIZE: print("ERROR: INVALID PAYLOAD LENGTH", len(payload), payload) return self.stoneStates = [] self.head = payload[0] self.tail = payload[1] self.size = payload[2] self.timestamp = Conversion.uint8_array_to_uint32(payload[4:8]) expectedSizeOfStoneStates = self.size * STONE_STATE_PACKET_SIZE if expectedSizeOfStoneStates + 8 > MESH_STATE_PACKET_SIZE: print("ERROR: CANT FIT STONE STATE PACKETS IN MESSAGE", expectedSizeOfStoneStates) return for i in range(0, self.size): self.stoneStates.append( StoneStatePacket(payload[8 + i * STONE_STATE_PACKET_SIZE:8 + (i + 1) * STONE_STATE_PACKET_SIZE])) # deprecation is when a stone id has multiple entrees in the data. Checking for this makes sure we only use the latest one. self._checkForDeprecation()
def obtainTimestamp(fullTimeStamp, lsb): timestampBytes = Conversion.uint32_to_uint8_array(int(fullTimeStamp)) lsbBytes = Conversion.uint16_to_uint8_array(lsb) restoredTimestamp = Conversion.uint8_array_to_uint32( [lsbBytes[0], lsbBytes[1], timestampBytes[2], timestampBytes[3]]) return restoredTimestamp
def parseOpCode3_type0(serviceData, data): if len(data) == 16: # dataType = data[0] serviceData.stateOfExternalCrownstone = False serviceData.crownstoneId = data[1] serviceData.switchState = data[2] serviceData.flagsBitmask = data[3] # bitmask states bitmaskArray = Conversion.uint8_to_bit_array(serviceData.flagsBitmask) serviceData.dimmerReady = bitmaskArray[0] serviceData.dimmingAllowed = bitmaskArray[1] serviceData.hasError = bitmaskArray[2] serviceData.switchLocked = bitmaskArray[3] serviceData.timeIsSet = bitmaskArray[4] serviceData.switchCraftEnabled = bitmaskArray[5] serviceData.tapToToggleEnabled = bitmaskArray[6] serviceData.behaviourOverridden = bitmaskArray[7] serviceData.temperature = Conversion.uint8_to_int8(data[4]) powerFactor = Conversion.uint8_to_int8(data[5]) realPower = Conversion.uint16_to_int16( Conversion.uint8_array_to_uint16([data[6], data[7]])) serviceData.powerFactor = float(powerFactor) / 127.0 # we cannot have a 0 for a power factor.To avoid division by 0, we set it to be either 0.01 or -0.01 if 0 <= serviceData.powerFactor < 0.01: serviceData.powerFactor = 0.01 elif -0.01 < serviceData.powerFactor < 0: serviceData.powerFactor = -0.01 serviceData.powerUsageReal = float(realPower) / 8.0 serviceData.powerUsageApparent = serviceData.powerUsageReal / serviceData.powerFactor serviceData.accumulatedEnergy = Conversion.uint32_to_int32( Conversion.uint8_array_to_uint32( [data[8], data[9], data[10], data[11]])) serviceData.partialTimestamp = Conversion.uint8_array_to_uint16( [data[12], data[13]]) serviceData.uniqueIdentifier = serviceData.partialTimestamp if serviceData.timeIsSet: serviceData.timestamp = reconstructTimestamp( time.time(), serviceData.partialTimestamp) else: serviceData.timestamp = serviceData.partialTimestamp # this is now a counter globalBitmaskArray = Conversion.uint8_to_bit_array(data[14]) serviceData.behaviourEnabled = globalBitmaskArray[0] serviceData.validation = data[15]
def __init__(self, payload, channelIndex): if len(payload) < self.packetSize: print("ERROR: INVALID PAYLOAD LENGTH", len(payload), payload) return self.channelIndex = channelIndex self.pin = payload[0] self.range = Conversion.uint8_array_to_uint32(payload[1:1+4]) self.refPin = payload[5]
def __init__(self, payload): if len(payload) < self.packetSize: print("ERROR: INVALID PAYLOAD LENGTH", len(payload), payload) return self.timestampCounter = Conversion.uint8_array_to_uint32(payload[0:4]) self.samples = [] for i in range(4, self.packetSize, self.sampleSize): self.samples.append( Conversion.uint8_array_to_int16(payload[i:i + self.sampleSize]))
def decryptSessionNonce(inputData, key): if len(inputData) == 16: decrypted = EncryptionHandler.decryptECB(inputData, key) checksum = Conversion.uint8_array_to_uint32(decrypted) if checksum == CHECKSUM: return [decrypted[4], decrypted[5], decrypted[6], decrypted[7], decrypted[8]] else: raise BluenetBleException(BleError.COULD_NOT_VALIDATE_SESSION_NONCE, "Could not validate the session nonce.") else: raise BluenetBleException(BleError.COULD_NOT_READ_SESSION_NONCE, "Could not read session nonce, maybe encryption is disabled?")
def generateMeshAccessAddress(retries=0): randomArray = [ random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) ] if not Util.validateMeshAccessAddress(randomArray) and retries < 100: retries += 1 return Util.generateMeshAccessAddress(retries) return Conversion.uint8_array_to_uint32(randomArray)
def parseOpcode4_type0(serviceData, data): if len(data) == 17: # opCode = data[0] # dataType = data[1] serviceData.switchState = data[2] serviceData.flagsBitmask = data[3] # bitmask states bitmaskArray = Conversion.uint8_to_bit_array(serviceData.flagsBitmask) serviceData.dimmingAvailable = bitmaskArray[0] serviceData.dimmingAllowed = bitmaskArray[1] serviceData.hasError = bitmaskArray[2] serviceData.switchLocked = bitmaskArray[3] serviceData.timeIsSet = bitmaskArray[4] serviceData.temperature = Conversion.uint8_to_int8(data[4]) powerFactor = Conversion.uint8_to_int8(data[5]) realPower = Conversion.uint16_to_int16(Conversion.uint8_array_to_uint16([data[6], data[7]])) serviceData.powerFactor = float(powerFactor) / 127.0 # we cannot have a 0 for a power factor.To avoid division by 0, we set it to be either 0.01 or -0.01 if 0 <= serviceData.powerFactor < 0.01: serviceData.powerFactor = 0.01 elif -0.01 < serviceData.powerFactor < 0: serviceData.powerFactor = -0.01 serviceData.powerUsageReal = float(realPower) / 8.0 serviceData.powerUsageApparent = serviceData.powerUsageReal / serviceData.powerFactor serviceData.accumulatedEnergy = Conversion.uint32_to_int32( Conversion.uint8_array_to_uint32([ data[8], data[9], data[10], data[11] ]) ) serviceData.uniqueIdentifier = data[12]
def __init__(self, payload): if len(payload) < 1: print("ERROR: INVALID PAYLOAD LENGTH", len(payload), payload) return index = 0 self.amountOfChannels = payload[index] index += 1 self.packetSize = self.amountOfChannels * self.channelSize + 1 + 4 # 4 for sampling period, 1 for count if len(payload) < self.packetSize: print("ERROR: INVALID PAYLOAD LENGTH", len(payload), payload) return for i in range(0, self.amountOfChannels): self.channels.append( AdcChannelPacket(payload[index:index + self.channelSize], i)) index += self.channelSize self.samplingPeriod = Conversion.uint8_array_to_uint32( payload[index:index + 4])
def validateMeshAccessAddress(randomArray): address = Conversion.uint8_array_to_uint32(randomArray) bitArray = Conversion.uint32_to_bit_array(address) bitArrayAdvertising = Conversion.uint32_to_bit_array( ADVERTISING_ACCESS_ADDRESS) # Requirement: It shall not be the advertising channel packets’ Access Address. if address == ADVERTISING_ACCESS_ADDRESS: # print("1") return False # Requirement: It shall not have all four octets equal. if randomArray[0] == randomArray[1] and randomArray[0] == randomArray[ 2] and randomArray[0] == randomArray[3]: # print("2") return False # Requirement: It shall not be a sequence that differs from the advertising channel packets’ Access Address by only one bit. diffCount = 0 for idx, bit in enumerate(bitArray): if bit != bitArrayAdvertising[idx]: diffCount += 1 if diffCount > 1: break if diffCount <= 1: # print("3") return False # Requirement: It shall have no more than six consecutive zeros or ones. consZero = 0 consOne = 0 for bit in bitArray: if bit: consOne += 1 consZero = 0 else: consZero += 1 consOne = 0 if consOne > 5 or consZero > 5: break if consOne > 5 or consZero > 5: # print("4") return False # Requirement: It shall have no more than 24 transitions. transitions = 0 for i in range(1, len(bitArray)): if bitArray[i] != bitArray[i - 1]: transitions += 1 if transitions > 24: # print("5") return False # Requirement: It shall have a minimum of two transitions in the most significant six bits. transitions = 0 for i in range(1, 6): if bitArray[i] != bitArray[i - 1]: transitions += 1 if transitions < 2: # print("6") return False transitions = 0 for i in range(len(bitArray) - 5, len(bitArray)): if bitArray[i] != bitArray[i - 1]: transitions += 1 if transitions < 2: # print("7") return False return True
def getUInt32(self): return Conversion.uint8_array_to_uint32(self._request(4))
def getTime(self): bytesResult = self._getState(StateType.TIME) return Conversion.uint8_array_to_uint32(bytesResult)