sburke781 / hubitat

Hubitat apps and device drivers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MEL - Local Control

sburke781 opened this issue · comments

Expand the Unified Thermostat Drivers to include local control of air conditioning units, removing the reliance on the cloud services.

  • Store Password as State Variable
  • Store Crypto Serial as State Variable
  • Store Device IP Address as State Variable
  • Add Local Control Preference Setting to unit child driver
  • Develop Encode Command method for Kumo platform
  • Develop unitCommand_KumoCloud_Local method to issue local commands
  • Update heat_KumoCloud and cool_KumoCloud methods to call local unit command method above
  • Update setTemperature_KumoCloud and setThermostatFanMode_KumoCloud methods
  • Update on_KumoCloud and off_KumoCloud methods
  • Update retrieveSettings_KumoCloud method
  • Update retrieveStatusInfo_KumoCloud method
  • Include Device IP Override command

Following communications issues for @bikesquid in recent days, I have taken another look at Fraser Sim's Typescript example for Homebridge and have made some note's on the encode token method:

// calcuate a token - based on pykumo and homebridge-kumo-local
const W = this.h2l(KUMO_KEY);
const p = base64.decode(password);

const data_hash = sjcl.codec.hex.fromBits(
  sjcl.hash.sha256.hash(
    sjcl.codec.hex.toBits(
      this.l2h(
        Array.prototype.map.call(p + post_data, (m2) => {
          return m2.charCodeAt(0);
        }),
      ),
    ),
  ),
);
// convert data_hash to byteArray
const data_hash_byteArray = this.h2l(data_hash);

// Part # 1 - Decode the password

const p = base64.decode(password);

Returns a byte[] or string(?) of decoded password string from Kumo

// Encode
def encoded = text.bytes.encodeBase64().toString()
println encoded

// Decode
byte[] decoded = encoded.decodeBase64()
println new String(decoded)

// Part # 2

SHA256 Hash

Could originalString below just be = password.decodeBase64() + post_data

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(
originalString.getBytes(StandardCharsets.UTF_8));

producing data_hash_byteArray from Typescript example

// Part # 3

Mashing together data_hash_byteArray, Kumo Key and CryptoSerial

const intermediate = new Uint8Array(88);
for (let i = 0; i < 32; i++) {
intermediate[i] = W[i];
intermediate[i + 32] = data_hash_byteArray[i];
}
intermediate[64] = 8;
intermediate[65] = 64;
intermediate[66] = 0; //S_PARAM

// convert cryptoSerial to byte array
const cryptoserial = this.h2l(cryptoSerial);

intermediate[79] = cryptoserial[8];
for (let i = 0; i < 4; i++) {
  intermediate[i + 80] = cryptoserial[i + 4];
  intermediate[i + 84] = cryptoserial[i];
}

This should all translate easily as quivalent array manipulation in Groovy

// Part # 4

Do SHA-256 hash of intermediate Byte array

const hash = sjcl.codec.hex.fromBits(
sjcl.hash.sha256.hash(sjcl.codec.hex.toBits(this.l2h(intermediate))),
);
return hash;

Use Message Digest again.... Without needing to convert a string to a byte array

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(
originalString.getBytes(StandardCharsets.UTF_8));

Convert encodedHash[] to Hex String

private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}