1- import fs from 'node:fs' ;
1+ import { existsSync , readFileSync , writeFileSync } from 'node:fs' ;
22
33import objectAssignDeep from 'object-assign-deep' ;
44
@@ -7,9 +7,8 @@ import logger from './util/logger';
77import * as settings from './util/settings' ;
88import utils from './util/utils' ;
99
10- const saveInterval = 1000 * 60 * 5 ; // 5 minutes
11-
12- const dontCacheProperties = [
10+ const SAVE_INTERVAL = 1000 * 60 * 5 ; // 5 minutes
11+ const CACHE_IGNORE_PROPERTIES = [
1312 'action' ,
1413 'action_.*' ,
1514 'button' ,
@@ -32,8 +31,8 @@ const dontCacheProperties = [
3231] ;
3332
3433class State {
35- private state : { [ s : string | number ] : KeyValue } = { } ;
36- private file = data . joinPath ( 'state.json' ) ;
34+ private readonly state = new Map < string | number , KeyValue > ( ) ;
35+ private readonly file = data . joinPath ( 'state.json' ) ;
3736 private timer ?: NodeJS . Timeout ;
3837
3938 constructor (
@@ -48,26 +47,37 @@ class State {
4847 this . load ( ) ;
4948
5049 // Save the state on every interval
51- this . timer = setInterval ( ( ) => this . save ( ) , saveInterval ) ;
50+ this . timer = setInterval ( ( ) => this . save ( ) , SAVE_INTERVAL ) ;
5251 }
5352
5453 stop ( ) : void {
5554 // Remove any invalid states (ie when the device has left the network) when the system is stopped
56- for ( const key in this . state ) {
57- if ( typeof key === 'string' && ! this . zigbee . resolveEntity ( key ) ) {
55+ for ( const [ key ] of this . state ) {
56+ if ( typeof key === 'string' && key . startsWith ( '0x' ) && ! this . zigbee . resolveEntity ( key ) ) {
5857 // string key = ieeeAddr
59- delete this . state [ key ] ;
58+ this . state . delete ( key ) ;
6059 }
6160 }
6261
6362 clearTimeout ( this . timer ) ;
6463 this . save ( ) ;
6564 }
6665
66+ clear ( ) : void {
67+ this . state . clear ( ) ;
68+ }
69+
6770 private load ( ) : void {
68- if ( fs . existsSync ( this . file ) ) {
71+ this . state . clear ( ) ;
72+
73+ if ( existsSync ( this . file ) ) {
6974 try {
70- this . state = JSON . parse ( fs . readFileSync ( this . file , 'utf8' ) ) ;
75+ const stateObj = JSON . parse ( readFileSync ( this . file , 'utf8' ) ) as KeyValue ;
76+
77+ for ( const key in stateObj ) {
78+ this . state . set ( key . startsWith ( '0x' ) ? key : Number . parseInt ( key , 10 ) , stateObj [ key ] ) ;
79+ }
80+
7181 logger . debug ( `Loaded state from file ${ this . file } ` ) ;
7282 } catch ( error ) {
7383 logger . debug ( `Failed to load state from file ${ this . file } (corrupt file?) (${ ( error as Error ) . message } )` ) ;
@@ -80,9 +90,11 @@ class State {
8090 private save ( ) : void {
8191 if ( settings . get ( ) . advanced . cache_state_persistent ) {
8292 logger . debug ( `Saving state to file ${ this . file } ` ) ;
83- const json = JSON . stringify ( this . state , null , 4 ) ;
93+
94+ const json = JSON . stringify ( Object . fromEntries ( this . state ) , null , 4 ) ;
95+
8496 try {
85- fs . writeFileSync ( this . file , json , 'utf8' ) ;
97+ writeFileSync ( this . file , json , 'utf8' ) ;
8698 } catch ( error ) {
8799 logger . error ( `Failed to write state to '${ this . file } ' (${ error } )` ) ;
88100 }
@@ -92,28 +104,28 @@ class State {
92104 }
93105
94106 exists ( entity : Device | Group ) : boolean {
95- return this . state [ entity . ID ] !== undefined ;
107+ return this . state . has ( entity . ID ) ;
96108 }
97109
98110 get ( entity : Group | Device ) : KeyValue {
99- return this . state [ entity . ID ] || { } ;
111+ return this . state . get ( entity . ID ) || { } ;
100112 }
101113
102114 set ( entity : Group | Device , update : KeyValue , reason ?: string ) : KeyValue {
103- const fromState = this . state [ entity . ID ] || { } ;
115+ const fromState = this . state . get ( entity . ID ) || { } ;
104116 const toState = objectAssignDeep ( { } , fromState , update ) ;
105117 const newCache = { ...toState } ;
106118 const entityDontCacheProperties = entity . options . filtered_cache || [ ] ;
107119
108- utils . filterProperties ( dontCacheProperties . concat ( entityDontCacheProperties ) , newCache ) ;
120+ utils . filterProperties ( CACHE_IGNORE_PROPERTIES . concat ( entityDontCacheProperties ) , newCache ) ;
109121
110- this . state [ entity . ID ] = newCache ;
122+ this . state . set ( entity . ID , newCache ) ;
111123 this . eventBus . emitStateChange ( { entity, from : fromState , to : toState , reason, update} ) ;
112124 return toState ;
113125 }
114126
115- remove ( ID : string | number ) : void {
116- delete this . state [ ID ] ;
127+ remove ( ID : string | number ) : boolean {
128+ return this . state . delete ( ID ) ;
117129 }
118130}
119131
0 commit comments