mirror of
				https://github.com/bourquep/mysa2mqtt.git
				synced 2025-10-31 19:59:42 +00:00 
			
		
		
		
	Compare commits
	
		
			25 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3b5dafeda9 | ||
|   | 35f2effe9c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | c29eae97ed | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 6502b76b77 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 39d0e64dc0 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 49c7a0fd8e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 36539b17b1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e1bd2e3a91 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | be163eddca | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 8a8ab7ab07 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a2f47220bd | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ec166cce61 | ||
|   | d2f7c73d84 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1bfb7e3add | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e9f2335c38 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 96114d2e91 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 21bc257b22 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 16a82f93f4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a95aee6c27 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 20b2866ee4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | dd23fca857 | ||
|   | 374dae1885 | ||
|   | 2e2e64d2d0 | ||
|   | 57502c5fb7 | ||
|   | 4895828426 | 
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -63,6 +63,8 @@ jobs: | |||||||
|     if: github.event_name == 'workflow_dispatch' |     if: github.event_name == 'workflow_dispatch' | ||||||
|     needs: build |     needs: build | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|  |     outputs: | ||||||
|  |       version: ${{ steps.version.outputs.version }} | ||||||
|     permissions: |     permissions: | ||||||
|       contents: write # to be able to publish a GitHub release |       contents: write # to be able to publish a GitHub release | ||||||
|       issues: write # to be able to comment on released issues |       issues: write # to be able to comment on released issues | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| # mysa2mqtt | # mysa2mqtt | ||||||
|  |  | ||||||
| [](https://www.npmjs.com/package/mysa2mqtt) | [](https://www.npmjs.com/package/mysa2mqtt) | ||||||
|  | [](https://hub.docker.com/r/bourquep/mysa2mqtt) | ||||||
| [](https://github.com/bourquep/mysa2mqtt/actions/workflows/github-code-scanning/codeql) | [](https://github.com/bourquep/mysa2mqtt/actions/workflows/github-code-scanning/codeql) | ||||||
| [](https://github.com/bourquep/mysa2mqtt/actions/workflows/ci.yml) | [](https://github.com/bourquep/mysa2mqtt/actions/workflows/ci.yml) | ||||||
|  |  | ||||||
| @@ -17,13 +18,13 @@ home automation platforms. | |||||||
|  |  | ||||||
| ## Supported hardware | ## Supported hardware | ||||||
|  |  | ||||||
| | Model Number | Description                                               | Supported                                                        | | | Model Number | Description                                               | Supported                                                            | | ||||||
| | ------------ | --------------------------------------------------------- | ---------------------------------------------------------------- | | | ------------ | --------------------------------------------------------- | -------------------------------------------------------------------- | | ||||||
| | `BB-V1-X`    | Mysa Smart Thermostat for Electric Baseboard Heaters V1   | ✅ Tested and working                                            | | | `BB-V1-X`    | Mysa Smart Thermostat for Electric Baseboard Heaters V1   | ✅ Tested and working                                                | | ||||||
| | `BB-V2-X`    | Mysa Smart Thermostat for Electric Baseboard Heaters V2   | ⚠️ Should work but not tested                                    | | | `BB-V2-X`    | Mysa Smart Thermostat for Electric Baseboard Heaters V2   | ⚠️ Partially working, in progress                                    | | ||||||
| | `BB-V2-X-L`  | Mysa Smart Thermostat LITE for Electric Baseboard Heaters | ⚠️ Should work but not tested; does not report power consumption | | | `BB-V2-X-L`  | Mysa Smart Thermostat LITE for Electric Baseboard Heaters | ⚠️ Partially working, in progress; does not report power consumption | | ||||||
| | `unknown`    | Mysa Smart Thermostat for Electric In-Floor Heating       | ⚠️ Should work but not tested                                    | | | `unknown`    | Mysa Smart Thermostat for Electric In-Floor Heating       | ⚠️ Should work but not tested                                        | | ||||||
| | `AC-V1-X`    | Mysa Smart Thermostat for Mini-Split Heat Pumps & AC      | 🚫 Not supported (yet)                                           | | | `AC-V1-X`    | Mysa Smart Thermostat for Mini-Split Heat Pumps & AC      | 🚫 Not supported (yet)                                               | | ||||||
|  |  | ||||||
| ## Disclaimer | ## Disclaimer | ||||||
|  |  | ||||||
| @@ -212,13 +213,11 @@ When using Home Assistant, devices will be automatically discovered and appear i | |||||||
| ### Common Issues | ### Common Issues | ||||||
|  |  | ||||||
| 1. **Authentication Failures** | 1. **Authentication Failures** | ||||||
|  |  | ||||||
|    - Verify your Mysa username and password |    - Verify your Mysa username and password | ||||||
|    - Check if session.json exists and is valid |    - Check if session.json exists and is valid | ||||||
|    - Try deleting session.json to force re-authentication |    - Try deleting session.json to force re-authentication | ||||||
|  |  | ||||||
| 2. **MQTT Connection Issues** | 2. **MQTT Connection Issues** | ||||||
|  |  | ||||||
|    - Verify MQTT broker hostname and port |    - Verify MQTT broker hostname and port | ||||||
|    - Check MQTT credentials if authentication is required |    - Check MQTT credentials if authentication is required | ||||||
|    - Ensure the MQTT broker is accessible from your network |    - Ensure the MQTT broker is accessible from your network | ||||||
| @@ -363,7 +362,6 @@ copyright notice and license text in any copy of the software or substantial por | |||||||
| ## Acknowledgments | ## Acknowledgments | ||||||
|  |  | ||||||
| - [mysa-js-sdk](https://github.com/bourquep/mysa-js-sdk) - Mysa API client library | - [mysa-js-sdk](https://github.com/bourquep/mysa-js-sdk) - Mysa API client library | ||||||
|  |  | ||||||
|   - This library would not be possible without the amazing work by [@dlenski](https://github.com/dlenski) in his |   - This library would not be possible without the amazing work by [@dlenski](https://github.com/dlenski) in his | ||||||
|     [mysotherm](https://github.com/dlenski/mysotherm) repository. He's the one who reversed-engineered the Mysa MQTT |     [mysotherm](https://github.com/dlenski/mysotherm) repository. He's the one who reversed-engineered the Mysa MQTT | ||||||
|     protocol which is being used by this library. |     protocol which is being used by this library. | ||||||
|   | |||||||
							
								
								
									
										2298
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2298
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										38
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								package.json
									
									
									
									
									
								
							| @@ -46,29 +46,29 @@ | |||||||
|     "build": "tsup" |     "build": "tsup" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "commander": "14.0.0", |     "commander": "14.0.1", | ||||||
|     "dotenv": "16.5.0", |     "dotenv": "17.2.3", | ||||||
|     "mqtt2ha": "4.0.0", |     "mqtt2ha": "4.1.2", | ||||||
|     "mysa-js-sdk": "1.1.2", |     "mysa-js-sdk": "1.3.3", | ||||||
|     "pino": "9.7.0", |     "pino": "10.0.0", | ||||||
|     "pino-pretty": "13.0.0" |     "pino-pretty": "13.1.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@commander-js/extra-typings": "14.0.0", |     "@commander-js/extra-typings": "14.0.0", | ||||||
|     "@eslint/js": "9.28.0", |     "@eslint/js": "9.37.0", | ||||||
|     "@semantic-release/npm": "12.0.1", |     "@semantic-release/npm": "12.0.2", | ||||||
|     "@types/node": "22.15.30", |     "@types/node": "24.6.2", | ||||||
|     "conventional-changelog-conventionalcommits": "9.0.0", |     "conventional-changelog-conventionalcommits": "9.1.0", | ||||||
|     "eslint": "9.28.0", |     "eslint": "9.37.0", | ||||||
|     "eslint-plugin-jsdoc": "50.7.1", |     "eslint-plugin-jsdoc": "60.8.1", | ||||||
|     "eslint-plugin-tsdoc": "0.4.0", |     "eslint-plugin-tsdoc": "0.4.0", | ||||||
|     "prettier": "3.5.3", |     "prettier": "3.6.2", | ||||||
|     "prettier-plugin-jsdoc": "1.3.2", |     "prettier-plugin-jsdoc": "1.3.3", | ||||||
|     "prettier-plugin-organize-imports": "4.1.0", |     "prettier-plugin-organize-imports": "4.3.0", | ||||||
|     "semantic-release": "24.2.5", |     "semantic-release": "24.2.9", | ||||||
|     "tsup": "8.5.0", |     "tsup": "8.5.0", | ||||||
|     "tsx": "4.19.4", |     "tsx": "4.20.6", | ||||||
|     "typescript": "5.8.3", |     "typescript": "5.9.3", | ||||||
|     "typescript-eslint": "8.33.1" |     "typescript-eslint": "8.45.0" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ SOFTWARE. | |||||||
| */ | */ | ||||||
|  |  | ||||||
| import { Logger } from 'mqtt2ha'; | import { Logger } from 'mqtt2ha'; | ||||||
| import { pino } from 'pino'; | import pino from 'pino'; | ||||||
|  |  | ||||||
| export class PinoLogger implements Logger { | export class PinoLogger implements Logger { | ||||||
|   constructor(private readonly logger: pino.Logger) {} |   constructor(private readonly logger: pino.Logger) {} | ||||||
| @@ -32,7 +32,7 @@ export class PinoLogger implements Logger { | |||||||
|     if (obj) { |     if (obj) { | ||||||
|       this.logger.debug(obj, message, ...meta); |       this.logger.debug(obj, message, ...meta); | ||||||
|     } else { |     } else { | ||||||
|       this.logger.debug(message, ...meta); |       this.logger.debug(null, message, ...meta); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -41,7 +41,7 @@ export class PinoLogger implements Logger { | |||||||
|     if (obj) { |     if (obj) { | ||||||
|       this.logger.info(obj, message, ...meta); |       this.logger.info(obj, message, ...meta); | ||||||
|     } else { |     } else { | ||||||
|       this.logger.info(message, ...meta); |       this.logger.info(null, message, ...meta); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -50,7 +50,7 @@ export class PinoLogger implements Logger { | |||||||
|     if (obj) { |     if (obj) { | ||||||
|       this.logger.warn(obj, message, ...meta); |       this.logger.warn(obj, message, ...meta); | ||||||
|     } else { |     } else { | ||||||
|       this.logger.warn(message, ...meta); |       this.logger.warn(null, message, ...meta); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -59,7 +59,7 @@ export class PinoLogger implements Logger { | |||||||
|     if (obj) { |     if (obj) { | ||||||
|       this.logger.error(obj, message, ...meta); |       this.logger.error(obj, message, ...meta); | ||||||
|     } else { |     } else { | ||||||
|       this.logger.error(message, ...meta); |       this.logger.error(null, message, ...meta); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/main.ts
									
									
									
									
									
								
							| @@ -64,8 +64,24 @@ async function main() { | |||||||
|     await client.login(options.mysaUsername, options.mysaPassword); |     await client.login(options.mysaUsername, options.mysaPassword); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   rootLogger.debug('Fetching devices and firmwares...'); | ||||||
|   const [devices, firmwares] = await Promise.all([client.getDevices(), client.getDeviceFirmwares()]); |   const [devices, firmwares] = await Promise.all([client.getDevices(), client.getDeviceFirmwares()]); | ||||||
|  |  | ||||||
|  |   rootLogger.debug('Fetching serial numbers...'); | ||||||
|  |   const serialNumbers = new Map<string, string>(); | ||||||
|  |   for (const [deviceId] of Object.entries(devices.DevicesObj)) { | ||||||
|  |     try { | ||||||
|  |       const serial = await client.getDeviceSerialNumber(deviceId); | ||||||
|  |       if (serial) { | ||||||
|  |         serialNumbers.set(deviceId, serial); | ||||||
|  |       } | ||||||
|  |     } catch (error) { | ||||||
|  |       rootLogger.error(error, `Failed to retrieve serial number for device ${deviceId}`); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   rootLogger.debug('Initializing MQTT entities...'); | ||||||
|  |  | ||||||
|   const mqttSettings: MqttSettings = { |   const mqttSettings: MqttSettings = { | ||||||
|     host: options.mqttHost, |     host: options.mqttHost, | ||||||
|     port: options.mqttPort, |     port: options.mqttPort, | ||||||
| @@ -82,7 +98,8 @@ async function main() { | |||||||
|         device, |         device, | ||||||
|         mqttSettings, |         mqttSettings, | ||||||
|         new PinoLogger(rootLogger.child({ module: 'thermostat', deviceId: device.Id })), |         new PinoLogger(rootLogger.child({ module: 'thermostat', deviceId: device.Id })), | ||||||
|         firmwares.Firmware[device.Id] |         firmwares.Firmware[device.Id], | ||||||
|  |         serialNumbers.get(device.Id) | ||||||
|       ) |       ) | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,7 +54,6 @@ function getPackageVersion(): string { | |||||||
|  * |  * | ||||||
|  * @param value - The value to parse. |  * @param value - The value to parse. | ||||||
|  * @returns The parsed integer value. |  * @returns The parsed integer value. | ||||||
|  * @throws InvalidArgumentError if the value is not a valid integer. |  | ||||||
|  */ |  */ | ||||||
| function parseRequiredInt(value: string) { | function parseRequiredInt(value: string) { | ||||||
|   const parsedValue = parseInt(value, 10); |   const parsedValue = parseInt(value, 10); | ||||||
| @@ -64,6 +63,8 @@ function parseRequiredInt(value: string) { | |||||||
|   return parsedValue; |   return parsedValue; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export const version = getPackageVersion(); | ||||||
|  |  | ||||||
| const extraHelpText = ` | const extraHelpText = ` | ||||||
| Copyright (c) 2025 Pascal Bourque | Copyright (c) 2025 Pascal Bourque | ||||||
| Licensed under the MIT License | Licensed under the MIT License | ||||||
| @@ -72,7 +73,7 @@ Source code and documentation available at: https://github.com/bourquep/mysa2mqt | |||||||
| `; | `; | ||||||
|  |  | ||||||
| export const options = new Command('mysa2mqtt') | export const options = new Command('mysa2mqtt') | ||||||
|   .version(getPackageVersion()) |   .version(version) | ||||||
|   .description('Expose Mysa smart thermostats to home automation platforms via MQTT.') |   .description('Expose Mysa smart thermostats to home automation platforms via MQTT.') | ||||||
|   .addHelpText('afterAll', extraHelpText) |   .addHelpText('afterAll', extraHelpText) | ||||||
|   .addOption( |   .addOption( | ||||||
|   | |||||||
| @@ -21,13 +21,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||||
| SOFTWARE. | SOFTWARE. | ||||||
| */ | */ | ||||||
|  |  | ||||||
| import { Climate, ClimateAction, DeviceConfiguration, Logger, MqttSettings, Sensor } from 'mqtt2ha'; | import { | ||||||
|  |   Climate, | ||||||
|  |   ClimateAction, | ||||||
|  |   DeviceConfiguration, | ||||||
|  |   Logger, | ||||||
|  |   MqttSettings, | ||||||
|  |   OriginConfiguration, | ||||||
|  |   Sensor | ||||||
|  | } from 'mqtt2ha'; | ||||||
| import { DeviceBase, FirmwareDevice, MysaApiClient, MysaDeviceMode, StateChange, Status } from 'mysa-js-sdk'; | import { DeviceBase, FirmwareDevice, MysaApiClient, MysaDeviceMode, StateChange, Status } from 'mysa-js-sdk'; | ||||||
|  | import { version } from './options'; | ||||||
|  |  | ||||||
| export class Thermostat { | export class Thermostat { | ||||||
|   private isStarted = false; |   private isStarted = false; | ||||||
|   private readonly mqttDevice: DeviceConfiguration; |   private readonly mqttDevice: DeviceConfiguration; | ||||||
|  |   private readonly mqttOrigin: OriginConfiguration; | ||||||
|   private readonly mqttClimate: Climate; |   private readonly mqttClimate: Climate; | ||||||
|  |   private readonly mqttTemperature: Sensor; | ||||||
|  |   private readonly mqttHumidity: Sensor; | ||||||
|   private readonly mqttPower: Sensor; |   private readonly mqttPower: Sensor; | ||||||
|  |  | ||||||
|   private readonly mysaStatusUpdateHandler = this.handleMysaStatusUpdate.bind(this); |   private readonly mysaStatusUpdateHandler = this.handleMysaStatusUpdate.bind(this); | ||||||
| @@ -38,14 +50,22 @@ export class Thermostat { | |||||||
|     public readonly mysaDevice: DeviceBase, |     public readonly mysaDevice: DeviceBase, | ||||||
|     private readonly mqttSettings: MqttSettings, |     private readonly mqttSettings: MqttSettings, | ||||||
|     private readonly logger: Logger, |     private readonly logger: Logger, | ||||||
|     public readonly mysaDeviceFirmware?: FirmwareDevice |     public readonly mysaDeviceFirmware?: FirmwareDevice, | ||||||
|  |     public readonly mysaDeviceSerialNumber?: string | ||||||
|   ) { |   ) { | ||||||
|     this.mqttDevice = { |     this.mqttDevice = { | ||||||
|       identifiers: mysaDevice.Id, |       identifiers: mysaDevice.Id, | ||||||
|       name: mysaDevice.Name, |       name: mysaDevice.Name, | ||||||
|       manufacturer: 'Mysa', |       manufacturer: 'Mysa', | ||||||
|       model: mysaDevice.Model, |       model: mysaDevice.Model, | ||||||
|       sw_version: mysaDeviceFirmware?.InstalledVersion |       sw_version: mysaDeviceFirmware?.InstalledVersion, | ||||||
|  |       serial_number: mysaDeviceSerialNumber | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     this.mqttOrigin = { | ||||||
|  |       name: 'mysa2mqtt', | ||||||
|  |       sw_version: version, | ||||||
|  |       support_url: 'https://github.com/bourquep/mysa2mqtt' | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.mqttClimate = new Climate( |     this.mqttClimate = new Climate( | ||||||
| @@ -55,6 +75,7 @@ export class Thermostat { | |||||||
|         component: { |         component: { | ||||||
|           component: 'climate', |           component: 'climate', | ||||||
|           device: this.mqttDevice, |           device: this.mqttDevice, | ||||||
|  |           origin: this.mqttOrigin, | ||||||
|           unique_id: `mysa_${mysaDevice.Id}_climate`, |           unique_id: `mysa_${mysaDevice.Id}_climate`, | ||||||
|           name: 'Thermostat', |           name: 'Thermostat', | ||||||
|           min_temp: mysaDevice.MinSetpoint, |           min_temp: mysaDevice.MinSetpoint, | ||||||
| @@ -104,13 +125,49 @@ export class Thermostat { | |||||||
|       } |       } | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     this.mqttTemperature = new Sensor({ | ||||||
|  |       mqtt: this.mqttSettings, | ||||||
|  |       logger: this.logger, | ||||||
|  |       component: { | ||||||
|  |         component: 'sensor', | ||||||
|  |         device: this.mqttDevice, | ||||||
|  |         origin: this.mqttOrigin, | ||||||
|  |         unique_id: `mysa_${mysaDevice.Id}_temperature`, | ||||||
|  |         name: 'Current temperature', | ||||||
|  |         device_class: 'temperature', | ||||||
|  |         state_class: 'measurement', | ||||||
|  |         unit_of_measurement: '°C', | ||||||
|  |         suggested_display_precision: 1, | ||||||
|  |         force_update: true | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     this.mqttHumidity = new Sensor({ | ||||||
|  |       mqtt: this.mqttSettings, | ||||||
|  |       logger: this.logger, | ||||||
|  |       component: { | ||||||
|  |         component: 'sensor', | ||||||
|  |         device: this.mqttDevice, | ||||||
|  |         origin: this.mqttOrigin, | ||||||
|  |         unique_id: `mysa_${mysaDevice.Id}_humidity`, | ||||||
|  |         name: 'Current humidity', | ||||||
|  |         device_class: 'humidity', | ||||||
|  |         state_class: 'measurement', | ||||||
|  |         unit_of_measurement: '%', | ||||||
|  |         suggested_display_precision: 0, | ||||||
|  |         force_update: true | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     this.mqttPower = new Sensor({ |     this.mqttPower = new Sensor({ | ||||||
|       mqtt: this.mqttSettings, |       mqtt: this.mqttSettings, | ||||||
|       logger: this.logger, |       logger: this.logger, | ||||||
|       component: { |       component: { | ||||||
|         component: 'sensor', |         component: 'sensor', | ||||||
|         device: this.mqttDevice, |         device: this.mqttDevice, | ||||||
|  |         origin: this.mqttOrigin, | ||||||
|         unique_id: `mysa_${mysaDevice.Id}_power`, |         unique_id: `mysa_${mysaDevice.Id}_power`, | ||||||
|  |         name: 'Current power', | ||||||
|         device_class: 'power', |         device_class: 'power', | ||||||
|         state_class: 'measurement', |         state_class: 'measurement', | ||||||
|         unit_of_measurement: 'W', |         unit_of_measurement: 'W', | ||||||
| @@ -136,9 +193,14 @@ export class Thermostat { | |||||||
|       this.mqttClimate.currentMode = state.TstatMode.v === 1 ? 'off' : state.TstatMode.v === 3 ? 'heat' : undefined; |       this.mqttClimate.currentMode = state.TstatMode.v === 1 ? 'off' : state.TstatMode.v === 3 ? 'heat' : undefined; | ||||||
|       this.mqttClimate.currentAction = this.computeCurrentAction(undefined, state.Duty.v); |       this.mqttClimate.currentAction = this.computeCurrentAction(undefined, state.Duty.v); | ||||||
|       this.mqttClimate.targetTemperature = this.mqttClimate.currentMode !== 'off' ? state.SetPoint.v : undefined; |       this.mqttClimate.targetTemperature = this.mqttClimate.currentMode !== 'off' ? state.SetPoint.v : undefined; | ||||||
|  |  | ||||||
|       await this.mqttClimate.writeConfig(); |       await this.mqttClimate.writeConfig(); | ||||||
|  |  | ||||||
|  |       await this.mqttTemperature.setState('state_topic', state.CorrectedTemp.v.toFixed(2)); | ||||||
|  |       await this.mqttTemperature.writeConfig(); | ||||||
|  |  | ||||||
|  |       await this.mqttHumidity.setState('state_topic', state.Humidity.v.toFixed(2)); | ||||||
|  |       await this.mqttHumidity.writeConfig(); | ||||||
|  |  | ||||||
|       // `state.Current.v` always has a non-zero value, even for thermostats that are off, so we can't use it to determine initial power state. |       // `state.Current.v` always has a non-zero value, even for thermostats that are off, so we can't use it to determine initial power state. | ||||||
|       await this.mqttPower.setState('state_topic', 'None'); |       await this.mqttPower.setState('state_topic', 'None'); | ||||||
|       await this.mqttPower.writeConfig(); |       await this.mqttPower.writeConfig(); | ||||||
| @@ -166,6 +228,8 @@ export class Thermostat { | |||||||
|     this.mysaApiClient.emitter.off('stateChanged', this.mysaStateChangeHandler); |     this.mysaApiClient.emitter.off('stateChanged', this.mysaStateChangeHandler); | ||||||
|  |  | ||||||
|     await this.mqttPower.setState('state_topic', 'None'); |     await this.mqttPower.setState('state_topic', 'None'); | ||||||
|  |     await this.mqttTemperature.setState('state_topic', 'None'); | ||||||
|  |     await this.mqttHumidity.setState('state_topic', 'None'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async handleMysaStatusUpdate(status: Status) { |   private async handleMysaStatusUpdate(status: Status) { | ||||||
| @@ -184,6 +248,9 @@ export class Thermostat { | |||||||
|     } else { |     } else { | ||||||
|       await this.mqttPower.setState('state_topic', 'None'); |       await this.mqttPower.setState('state_topic', 'None'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     await this.mqttTemperature.setState('state_topic', status.temperature.toFixed(2)); | ||||||
|  |     await this.mqttHumidity.setState('state_topic', status.humidity.toFixed(2)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async handleMysaStateChange(state: StateChange) { |   private async handleMysaStateChange(state: StateChange) { | ||||||
| @@ -200,6 +267,7 @@ export class Thermostat { | |||||||
|  |  | ||||||
|       case 'heat': |       case 'heat': | ||||||
|         this.mqttClimate.currentMode = 'heat'; |         this.mqttClimate.currentMode = 'heat'; | ||||||
|  |         this.mqttClimate.targetTemperature = state.setPoint; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|     "noEmit": true, |     "noEmit": true, | ||||||
|     "resolveJsonModule": true, |     "resolveJsonModule": true, | ||||||
|     "module": "ESNext", |     "module": "ESNext", | ||||||
|  |     "lib": ["ES2022"], | ||||||
|     "paths": { |     "paths": { | ||||||
|       "@/*": ["./src/*"] |       "@/*": ["./src/*"] | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user