diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index e2a15b2..0994c49 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -33,11 +33,13 @@ jobs: uses: actions/checkout@v4 - name: Setup Pages uses: actions/configure-pages@v5 - - name: Upload artifact + + - name: Upload Artifact uses: actions/upload-pages-artifact@v3 with: - # Upload entire repository - path: './UI' + # Deploy UI folder + path: './UI/' + - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/UI/index.html b/UI/index.html index 7fc2088..f892c33 100644 --- a/UI/index.html +++ b/UI/index.html @@ -40,8 +40,15 @@

NeoDK control panel

-

- NeoDK ({{ device.state.PlayState }} | {{ device.state.Intensity }}% )

+

+ {{ device.Name || "(no name)"}} + |

+

+ +

+

+

({{ device.state.PlayState }} | {{ device.state.Intensity }}% )

+
Battery Voltage: {{ device.state.power.BatteryVoltage }}V
@@ -54,26 +61,6 @@

{{ device.state.Intensity }}

- -
 
@@ -124,7 +111,7 @@

Pattern: {{ device.state.CurrentPattern }} diff --git a/UI/neodk.js b/UI/neodk.js index fc1bc32..41e2660 100644 --- a/UI/neodk.js +++ b/UI/neodk.js @@ -19,6 +19,7 @@ class NeoDK { this.logger = logger; this.state = state; + this._name = ''; } /** @@ -41,17 +42,20 @@ class NeoDK { // Public methods + get Name() { + return this._name; + } + set Name(value) { + this._name = value; + this.#writeString(value, NeoDK.#AttributeId.BoxName) + } /** * Method to select pattern to play * @public * @param {string} name name of the pattern to select */ selectPattern(name) { - let enc_name = new TextEncoder().encode(name); - const array = Array.from(enc_name); // Convert to regular array - array.unshift(NeoDK.#Encoding.UTF8_1Len, enc_name.length); // Use unshift - enc_name = Uint8Array.from(array); - this.#sendAttrWriteRequest(this.#the_writer, NeoDK.#AttributeId.CurrentPatternName, enc_name); + this.#writeString(name, NeoDK.#AttributeId.CurrentPatternName); } /** @@ -60,11 +64,7 @@ class NeoDK { * @param {ChangeStateCommand} state one of the accepted play states from ChangeStateCommand */ setPlayState(state) { - let enc_state = new TextEncoder().encode(state); - const array = Array.from(enc_state); // Convert to regular array - array.unshift(NeoDK.#Encoding.UTF8_1Len, enc_state.length); // Use unshift - enc_state = Uint8Array.from(array); - this.#sendAttrWriteRequest(this.#the_writer, NeoDK.#AttributeId.PlayPauseStop, enc_state); + this.#writeString(state, NeoDK.#AttributeId.PlayPauseStop); } /** @@ -76,6 +76,10 @@ class NeoDK { this.#sendAttrWriteRequest(this.#the_writer, NeoDK.#AttributeId.IntensityPercent, new Uint8Array([NeoDK.#Encoding.UnsignedInt1, intensity])); } + refreshVoltages() { + this.#sendAttrReadRequest(this.#the_writer, NeoDK.#AttributeId.Voltages); + } + /** * Method to get the port from browser * User will be prompted to select a port that box is connected to @@ -102,16 +106,49 @@ class NeoDK { return await this.#usePort(port); } + static BoxPower = class NeoDKBoxPower { + /** + * + */ + constructor() { + this._batteryVoltage = 0; + this._capacitorVoltage = 0; + this._primaryCurrent = 0; + } + + get BatteryVoltage() { + return this._batteryVoltage; + } + set BatteryVoltage(value) { + this._batteryVoltage = value; + } + + get CapacitorVoltage() { + return this._capacitorVoltage; + } + set CapacitorVoltage(value) { + this._capacitorVoltage = value; + } + + get PrimaryCurrent() { + return this._primaryCurrent; + } + set PrimaryCurrent(value) { + this._primaryCurrent = value; + } + + } /** * Structure that represents play state of NeoDK */ - static State = class { + static State = class NeoDKState { constructor() { this._playState = NeoDK.#playStates[0]; this._intensity = 0; this._currentPattern = ''; this._availablePatterns = []; + this.power = new NeoDK.BoxPower(); } @@ -190,10 +227,12 @@ class NeoDK { * @readonly */ static #AttributeId = { + Voltages: 3, AllPatternNames: 5, CurrentPatternName: 6, IntensityPercent: 7, - PlayPauseStop: 8 + PlayPauseStop: 8, + BoxName: 9 }; /** @@ -205,6 +244,7 @@ class NeoDK { static #Encoding = { UnsignedInt1: 4, UTF8_1Len: 12, + Bytes_1Len: 16, Array: 22, EndOfContainer: 24 } @@ -248,6 +288,14 @@ class NeoDK { // private methods + #writeString(value, attribute) { + let enc_value = new TextEncoder().encode(value); + const array = Array.from(enc_value); // Convert to regular array + array.unshift(NeoDK.#Encoding.UTF8_1Len, enc_value.length); // Use unshift + enc_value = Uint8Array.from(array); + this.#sendAttrWriteRequest(this.#the_writer, attribute, enc_value); + } + #initFrame(payload_size, frame_type, service_type, seq) { const frame = new Uint8Array(NeoDK.#StructureSize.FrameHeader + payload_size); frame[0] = (service_type << 4) | (frame_type << 1); @@ -358,6 +406,17 @@ class NeoDK { let offset = NeoDK.#StructureSize.AttributeAction; const data_length = aa.length - offset; switch (attribute_id) { + case NeoDK.#AttributeId.Voltages: + if (data_length >= 8 && aa[offset] == NeoDK.#Encoding.Bytes_1Len) { + const Vbat_mV = aa[offset + 2] | ((aa[offset + 3]) << 8); + const Vcap_mV = aa[offset + 4] | ((aa[offset + 5]) << 8); + const Ipri_mA = aa[offset + 6] | ((aa[offset + 7]) << 8); + this.state.power.BatteryVoltage = Vbat_mV / 1000; + this.state.power.CapacitorVoltage = Vcap_mV / 1000; + this.state.power.PrimaryCurrent = Ipri_mA / 1000; + this.logger.log('Vbat=' + Vbat_mV + ' mV, Vcap=' + Vcap_mV + ' mV, Ipri=' + Ipri_mA + ' mA'); + } + break; case NeoDK.#AttributeId.AllPatternNames: if (data_length >= 2 && aa[offset] == NeoDK.#Encoding.Array) { this.logger.log('Available patterns:'); @@ -386,6 +445,13 @@ class NeoDK { this.logger.log('NeoDK is ' + NeoDK.#playStates[play_state]); } break; + case NeoDK.#AttributeId.BoxName: + if (aa[offset] == NeoDK.#Encoding.UTF8_1Len) { + const name = new TextDecoder().decode(aa.slice(offset + 2)); + this._name = name; + this.logger.log('Box name is ' + name); + } + break; default: this.logger.log('Unexpected attribute id: ' + attribute_id); } @@ -484,6 +550,8 @@ class NeoDK { // We have one readable attribute and three we can subscribe to. this.#sendAttrReadRequest(this.#the_writer, NeoDK.#AttributeId.AllPatternNames); + this.#sendAttrReadRequest(this.#the_writer, NeoDK.#AttributeId.Voltages); + this.#sendAttrReadRequest(this.#the_writer, NeoDK.#AttributeId.BoxName); this.#sendAttrSubscribeRequest(this.#the_writer, NeoDK.#AttributeId.CurrentPatternName); this.#sendAttrSubscribeRequest(this.#the_writer, NeoDK.#AttributeId.IntensityPercent); this.#sendAttrSubscribeRequest(this.#the_writer, NeoDK.#AttributeId.PlayPauseStop); @@ -514,4 +582,5 @@ class NeoDK { } } -export default NeoDK; \ No newline at end of file +export default NeoDK; + diff --git a/UI/script.js b/UI/script.js index 62bca46..f5899a0 100644 --- a/UI/script.js +++ b/UI/script.js @@ -1,9 +1,42 @@ import NeoDK from './neodk.js'; import { createApp, ref, toRaw, computed } from 'vue'; +class NeoDKBoxPowerVM extends NeoDK.BoxPower { + constructor() { + super(); + + } + + #batteryVoltage = ref(super._batteryVoltage); + #capacitorVoltage = ref(super._capacitorVoltage); + #primaryCurrent = ref(super._primaryCurrent); + + get BatteryVoltage() { + return toRaw(this).#batteryVoltage.value; + } + set BatteryVoltage(value){ + toRaw(this).#batteryVoltage.value = value; + } + + get CapacitorVoltage() { + return toRaw(this).#capacitorVoltage.value; + } + set CapacitorVoltage(value){ + toRaw(this).#capacitorVoltage.value = value; + } + + get PrimaryCurrent() { + return toRaw(this).#primaryCurrent.value; + } + set PrimaryCurrent(value){ + toRaw(this).#primaryCurrent.value = value; + } +} + class NeoDKStateVM extends NeoDK.State { constructor() { super(); + this.power = new NeoDKBoxPowerVM(); } #playState = ref(super._playState); @@ -41,6 +74,16 @@ class NeoDKStateVM extends NeoDK.State { } class NeoDKVM extends NeoDK { + + /** + * + */ + constructor(...args) { + super(...args); + this.changingName = ref(false); + this.newName = ''; + } + setIntensity = (intensity) => super.setIntensity(intensity); selectPattern = (pattern) => super.selectPattern(pattern); @@ -52,6 +95,16 @@ class NeoDKVM extends NeoDK { stop = () => super.setPlayState(NeoDK.ChangeStateCommand.stop); paused = computed(() => ['paused', 'stopped'].includes(this.state.PlayState)); + + changeName = () => { + this.changingName.value = true; + this.newName = this.Name; + } + + saveName = () => { + this.Name = this.newName; + this.changingName.value = false; + } } const app = createApp({ @@ -76,9 +129,9 @@ const app = createApp({ async refreshState() { try { this.devices.forEach(element => { - // todo add anything that needs to be periodically refreshed here + toRaw(element).refreshVoltages(); }); - //setTimeout(this.refreshState, 1000); + setTimeout(this.refreshState, 1000 * 60 * 5); } catch (error) { console.error('Failed to fetch state', error); }