6161
6262 .info-container {
6363 display : grid;
64- grid-template-columns : 100 px 1fr ;
64+ grid-template-columns : 190 px 1fr ;
6565 row-gap : 10px ;
6666 margin-top : 20px ;
6767 }
6868
69+ .topic-mono {
70+ font-family : monospace;
71+ font-size : 13px ;
72+ word-break : break-all;
73+ }
74+
6975 label {
7076 font-weight : bold;
7177 }
@@ -181,6 +187,7 @@ <h2>Debug Logs</h2>
181187 < div id ="logs " class ="logs "> </ div >
182188 </ div >
183189
190+ < script src ="https://cdn.jsdelivr.net/npm/aes-js@3.1.2/index.min.js "> </ script >
184191 < script >
185192 // BLE Service and Characteristic UUIDs
186193 const SERVICE_UUID = '0000ff00-0000-1000-8000-00805f9b34fb' ;
@@ -612,6 +619,23 @@ <h2>Debug Logs</h2>
612619 deviceType . textContent = parsedInfo . type || 'Unknown' ;
613620 deviceId . textContent = parsedInfo . id || 'Unknown' ;
614621 deviceMac . textContent = parsedInfo . mac || 'Unknown' ;
622+ // Always show both topics, and set correct threshold in label
623+ let mac = parsedInfo . mac || '' ;
624+ let type = parsedInfo . type || '' ;
625+ let macNoColons = mac . replace ( / : / g, '' ) . toLowerCase ( ) ;
626+ let encryptedMac = calculateNewVersionTopicId ( macNoColons ) ;
627+ let oldTopic = `hame_energy/${ type } /device/${ macNoColons } /ctrl` ;
628+ let newTopic = `marstek_energy/${ type } /device/${ encryptedMac } /ctrl` ;
629+ let oldAppTopic = `hame_energy/${ type } /App/${ macNoColons } /ctrl` ;
630+ let newAppTopic = `marstek_energy/${ type } /App/${ encryptedMac } /ctrl` ;
631+ // Determine threshold
632+ let threshold = / ^ H M J - \d + $ / i. test ( type ) ? 108 : 226 ;
633+ document . getElementById ( 'device-topic-ge-label' ) . textContent = `Topic for firmware >=${ threshold } :` ;
634+ document . getElementById ( 'device-topic-ge' ) . textContent = newTopic ;
635+ document . getElementById ( 'device-topic-ge-app' ) . textContent = newAppTopic ;
636+ document . getElementById ( 'device-topic-lt-label' ) . textContent = `Topic for firmware <${ threshold } :` ;
637+ document . getElementById ( 'device-topic-lt' ) . textContent = oldTopic ;
638+ document . getElementById ( 'device-topic-lt-app' ) . textContent = oldAppTopic ;
615639 deviceInfo . classList . remove ( 'hidden' ) ;
616640
617641 // Update status
@@ -745,8 +769,58 @@ <h2>Debug Logs</h2>
745769 }
746770 }
747771
748- // Function to analyze a connected device and provide filtering options
749- // This function was used for debugging but is now removed since we're using a hardcoded filter
772+ // --- Topic Calculation Helpers ---
773+ // AES key and IV for new topic
774+ const NEW_TOPIC_KEY = [ 33 , 64 , 35 , 36 , 37 , 94 , 38 , 42 , 40 , 41 , 95 , 43 , 123 , 125 , 91 , 93 ] ;
775+ const NEW_TOPIC_IV = new Array ( 16 ) . fill ( 0 ) ;
776+
777+ // Pad to 16 bytes (PKCS7)
778+ function pkcs7Pad ( bytes ) {
779+ const padLen = 16 - ( bytes . length % 16 ) ;
780+ const padded = new Uint8Array ( bytes . length + padLen ) ;
781+ padded . set ( bytes ) ;
782+ padded . fill ( padLen , bytes . length ) ;
783+ return padded ;
784+ }
785+
786+ // Calculate new version topic encrypted MAC (browser version)
787+ function calculateNewVersionTopicId ( mac ) {
788+ const aesCbc = new aesjs . ModeOfOperation . cbc ( NEW_TOPIC_KEY , NEW_TOPIC_IV ) ;
789+ // MAC is a string, encode as UTF-8 bytes
790+ const macBytes = aesjs . utils . utf8 . toBytes ( mac ) ;
791+ const padded = pkcs7Pad ( macBytes ) ;
792+ const encryptedBytes = aesCbc . encrypt ( padded ) ;
793+ return aesjs . utils . hex . fromBytes ( encryptedBytes ) ;
794+ }
795+
796+ // --- Topic Calculation Logic ---
797+ function getDeviceTopic ( type , version , mac ) {
798+ // type: string, version: string or number, mac: string
799+ // If device type is HMJ-X (X is an arbitrary number) and version >=108,
800+ // or if device type is not HMJ-X and version >=226, use new topic
801+ // else use old topic
802+ let topic = '' ;
803+ let encryptedMac = '' ;
804+ let macNoColons = ( mac || '' ) . replace ( / : / g, '' ) . toLowerCase ( ) ;
805+ let typeUpper = ( type || '' ) . toUpperCase ( ) ;
806+ let versionNum = parseInt ( version , 10 ) ;
807+ if ( / ^ H M J - \d + $ / i. test ( typeUpper ) ) {
808+ if ( versionNum >= 108 ) {
809+ encryptedMac = calculateNewVersionTopicId ( macNoColons ) ;
810+ topic = `marstek_energy/${ type } /device/${ encryptedMac } /ctrl` ;
811+ } else {
812+ topic = `hame_energy/${ type } /device/${ macNoColons } /ctrl` ;
813+ }
814+ } else {
815+ if ( versionNum >= 226 ) {
816+ encryptedMac = calculateNewVersionTopicId ( macNoColons ) ;
817+ topic = `marstek_energy/${ type } /device/${ encryptedMac } /ctrl` ;
818+ } else {
819+ topic = `hame_energy/${ type } /device/${ macNoColons } /ctrl` ;
820+ }
821+ }
822+ return topic ;
823+ }
750824
751825 // Event listeners
752826 connectButton . addEventListener ( 'click' , connectToDevice ) ;
@@ -772,6 +846,46 @@ <h2>Debug Logs</h2>
772846 // Display browser information
773847 log ( `Browser: ${ navigator . userAgent } ` ) ;
774848 }
849+
850+ // --- Add topic display to device info UI ---
851+ // Add rows for both topics and an info note, but use generic placeholders for now
852+ ( function addTopicRows ( ) {
853+ const infoContainer = document . querySelector ( '.info-container' ) ;
854+ // For >= threshold
855+ const topicGELabel = document . createElement ( 'div' ) ;
856+ topicGELabel . id = 'device-topic-ge-label' ;
857+ topicGELabel . textContent = 'Topic for firmware >=XXX:' ;
858+ const topicGEValue = document . createElement ( 'div' ) ;
859+ topicGEValue . id = 'device-topic-ge' ;
860+ topicGEValue . className = 'topic-mono' ;
861+ infoContainer . appendChild ( topicGELabel ) ;
862+ infoContainer . appendChild ( topicGEValue ) ;
863+ // App topic for >= threshold
864+ const topicGEAppLabel = document . createElement ( 'div' ) ;
865+ topicGEAppLabel . id = 'device-topic-ge-app-label' ;
866+ const topicGEAppValue = document . createElement ( 'div' ) ;
867+ topicGEAppValue . id = 'device-topic-ge-app' ;
868+ topicGEAppValue . className = 'topic-mono' ;
869+ infoContainer . appendChild ( topicGEAppLabel ) ;
870+ infoContainer . appendChild ( topicGEAppValue ) ;
871+ // For < threshold
872+ const topicLTLabel = document . createElement ( 'div' ) ;
873+ topicLTLabel . id = 'device-topic-lt-label' ;
874+ topicLTLabel . textContent = 'Topic for firmware <XXX:' ;
875+ const topicLTValue = document . createElement ( 'div' ) ;
876+ topicLTValue . id = 'device-topic-lt' ;
877+ topicLTValue . className = 'topic-mono' ;
878+ infoContainer . appendChild ( topicLTLabel ) ;
879+ infoContainer . appendChild ( topicLTValue ) ;
880+ // App topic for < threshold
881+ const topicLTAppLabel = document . createElement ( 'div' ) ;
882+ topicLTAppLabel . id = 'device-topic-lt-app-label' ;
883+ const topicLTAppValue = document . createElement ( 'div' ) ;
884+ topicLTAppValue . id = 'device-topic-lt-app' ;
885+ topicLTAppValue . className = 'topic-mono' ;
886+ infoContainer . appendChild ( topicLTAppLabel ) ;
887+ infoContainer . appendChild ( topicLTAppValue ) ;
888+ } ) ( ) ;
775889 </ script >
776890</ body >
777891</ html >
0 commit comments