diff --git a/CHANGELOG.md b/CHANGELOG.md index 3603a516b..285760fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ All notable changes to this project will be documented in this file. - Command ``DaliSend
|,`` to send command (address+256 is repeat) on DALI bus - Command ``DaliQuery
|,`` to send command (address+256 is repeat) on DALI bus and wait up to DALI_TIMEOUT ms for response - Berry Serial `config` to change parity on-the-fly for RS-485 (#22285) +- Misubishi Electric HVAC Heat/Dry/Cool Auto operation mode (#22216) +- Misubishi Electric HVAC Bridge to HomeBridge/Homekit locally (#22236) +- Misubishi Electric HVAC Air Direction Control (#22241) +- Misubishi Electric HVAC prohibit function (#22269) +- Misubishi Electric HVAC compressor map and operation power and energy (#22290) ### Changed - ESP32 platform update from 2024.09.10 to 2024.09.30 and Framework (Arduino Core) from v3.0.5 to v3.1.0.240926 (#22203) @@ -59,10 +64,6 @@ All notable changes to this project will be documented in this file. - ESP8266 experimental support for second I2C bus - Berry improve `int64` constructor (#22172) - MQTT warning if trying to connect without TLS on a port that normally uses TLS (#22175) -- Misubishi Electric HVAC Heat/Dry/Cool ISEE operation mode (#22216) -- Misubishi Electric HVAC Bridge to HomeBridge/Homekit locally (#22236) -- Misubishi Electric HVAC Air Direction Control (#22241) -- Misubishi Electric HVAC prohibit function (#22269) ### Changed - Refactored I2C drivers HTU21, BH1750, SHT3x, iAQ and HYT diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8f102ae0e..d65aa4e13 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -136,10 +136,11 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Support nexus protocol and calculation of separation limit to rc-switch library [#21886](https://github.com/arendst/Tasmota/issues/21886) - KNX additional KnxTx functions and define KNX_USE_DPT9 [#22071](https://github.com/arendst/Tasmota/issues/22071) - SML multi TRX line [#22056](https://github.com/arendst/Tasmota/issues/22056) -- Misubishi Electric HVAC Heat/Dry/Cool ISEE operation mode [#22216](https://github.com/arendst/Tasmota/issues/22216) +- Misubishi Electric HVAC Heat/Dry/Cool Auto operation mode [#22216](https://github.com/arendst/Tasmota/issues/22216) - Misubishi Electric HVAC Bridge to HomeBridge/Homekit locally [#22236](https://github.com/arendst/Tasmota/issues/22236) - Misubishi Electric HVAC Air Direction Control [#22241](https://github.com/arendst/Tasmota/issues/22241) - Misubishi Electric HVAC prohibit function [#22269](https://github.com/arendst/Tasmota/issues/22269) +- Misubishi Electric HVAC compressor map and operation power and energy [#22290](https://github.com/arendst/Tasmota/issues/22290) - Zigbee Koenkk firmware 20240710 for Sonoff Zigbee ZBPro [#22076](https://github.com/arendst/Tasmota/issues/22076) - Berry Zigbee improvements to prepare Matter [#22083](https://github.com/arendst/Tasmota/issues/22083) - Berry virtual Energy driver [#22134](https://github.com/arendst/Tasmota/issues/22134) diff --git a/tasmota/berry/animate_demo/leds_animation.be b/tasmota/berry/animate_demo/leds_animation.be index 68ea18495..cc76c80d2 100644 --- a/tasmota/berry/animate_demo/leds_animation.be +++ b/tasmota/berry/animate_demo/leds_animation.be @@ -24,43 +24,6 @@ class Leds_animation_UI "

") end - #- ---------------------------------------------------------------------- -# - #- Show page to migrate to factory layout + single OTA - #- ---------------------------------------------------------------------- -# - def show_migrate_to_factory(p) - # display ota partitions - import webserver - import string - - if !self.factory_migrate_eligible(p) return end - - webserver.content_send("
 Migrate to safeboot partition layout 

") - - webserver.content_send("

The `safeboot` layout allows for increased size
of firmware or file-system.

") - webserver.content_send("

Please see Safeboot layout documentation

") - webserver.content_send("

 

") - - webserver.content_send(string.format("

Step 1: %s

", self.display_step_state(self.test_step_1(p), "boot on `app1`"))) - webserver.content_send(string.format("

Step 2: %s

", self.display_step_state(self.test_step_2(p), "flash `safeboot` to `app0`"))) - webserver.content_send(string.format("

Step 3: %s

", self.display_step_state(self.test_step_3(p), "change partition map"))) - webserver.content_send(string.format("

Step 4: %s

", self.display_step_state(self.test_step_4(p), "flash final firmware"))) - - webserver.content_send("
") - var ota_url = tasmota.cmd("OtaUrl").find("OtaUrl", "") - webserver.content_send(string.format("
OTA Url

", - ota_url)) - - import persist - var safeboot_url = persist.find("safeboot_url", self.default_safeboot_URL()) - webserver.content_send(string.format("
SAFEBOOT Url (don't change)
", - safeboot_url)) - - webserver.content_send("

") - - webserver.content_send("

") - end - ####################################################################### # Show background colors ####################################################################### diff --git a/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino b/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino index 3b7e6da5d..1d9cd0ced 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino @@ -91,8 +91,14 @@ struct miel_hvac_data_roomtemp { struct miel_hvac_data_status { uint8_t _pad1[2]; + uint8_t _pad2[1]; uint8_t compressor; - uint8_t operation; +#define MIEL_HVAC_STATUS_COMPRESSOR_OFF 0x00 +#define MIEL_HVAC_STATUS_COMPRESSOR_ON 0x01 + uint8_t operationpower; + uint8_t operationpower1; + uint8_t operationenergy; + uint8_t operationenergy1; }; struct miel_hvac_data { @@ -130,6 +136,12 @@ CTASSERT(offsetof(struct miel_hvac_data, data.settings.airdirection) == 14); CTASSERT(offsetof(struct miel_hvac_data, data.roomtemp.temp) == 3); CTASSERT(offsetof(struct miel_hvac_data, data.roomtemp.temp05) == 6); +CTASSERT(offsetof(struct miel_hvac_data, data.status.compressor) == 4); +CTASSERT(offsetof(struct miel_hvac_data, data.status.operationpower) == 5); +CTASSERT(offsetof(struct miel_hvac_data, data.status.operationpower1) == 6); +CTASSERT(offsetof(struct miel_hvac_data, data.status.operationenergy) == 7); +CTASSERT(offsetof(struct miel_hvac_data, data.status.operationenergy1) == 8); + /* to hvac */ #define MIEL_HVAC_H_TYPE_CONNECT 0x5a @@ -206,7 +218,7 @@ struct miel_hvac_msg_update { uint8_t _pad1[4]; uint8_t widevane; #define MIEL_HVAC_UPDATE_WIDEVANE_MASK 0x0f -#define MIEL_HVAC_UPDATE_WIDEVANE_ISEE 0x00 +#define MIEL_HVAC_UPDATE_WIDEVANE_AUTO 0x00 #define MIEL_HVAC_UPDATE_WIDEVANE_LL 0x01 #define MIEL_HVAC_UPDATE_WIDEVANE_L 0x02 #define MIEL_HVAC_UPDATE_WIDEVANE_C 0x03 @@ -304,6 +316,11 @@ struct miel_hvac_map { const char *name; }; +static const struct miel_hvac_map miel_hvac_power_map[] = { + { MIEL_HVAC_UPDATE_POWER_OFF, "off" }, + { MIEL_HVAC_UPDATE_POWER_ON, "on" }, +}; + static const struct miel_hvac_map miel_hvac_mode_map[] = { { MIEL_HVAC_UPDATE_MODE_HEAT, "heat" }, { MIEL_HVAC_UPDATE_MODE_DRY, "dry" }, @@ -335,7 +352,7 @@ static const struct miel_hvac_map miel_hvac_vane_map[] = { }; static const struct miel_hvac_map miel_hvac_widevane_map[] = { - { MIEL_HVAC_UPDATE_WIDEVANE_ISEE, "isee" }, + { MIEL_HVAC_UPDATE_WIDEVANE_AUTO, "auto" }, { MIEL_HVAC_UPDATE_WIDEVANE_LL, "left" }, { MIEL_HVAC_UPDATE_WIDEVANE_L, "left_middle" }, { MIEL_HVAC_UPDATE_WIDEVANE_C, "center" }, @@ -362,6 +379,11 @@ static const struct miel_hvac_map miel_hvac_airdirection_map[] = { { MIEL_HVAC_UPDATE_AIRDIRECTION_DIRECT, "direct" }, }; +static const struct miel_hvac_map miel_hvac_compressor_map[] = { + { MIEL_HVAC_STATUS_COMPRESSOR_OFF, "off" }, + { MIEL_HVAC_STATUS_COMPRESSOR_ON, "on" }, +}; + enum miel_hvac_parser_state { MIEL_HVAC_P_START, MIEL_HVAC_P_TYPE, @@ -997,8 +1019,12 @@ miel_hvac_publish_settings(struct miel_hvac_softc *sc) const char *name; const char *name_swing_h; - Response_P(PSTR("{\"" D_JSON_IRHVAC_POWER "\":\"%s\""), - set->power ? "ON" : "OFF"); + name = miel_hvac_map_byval(set->power, + miel_hvac_power_map, nitems(miel_hvac_power_map)); + if (name != NULL) { + Response_P(PSTR("{\"" D_JSON_IRHVAC_POWER "\":\"%s\""), + name); + } name = miel_hvac_map_byval( set->mode & MIEL_HVAC_SETTINGS_MODE_MASK, @@ -1046,7 +1072,7 @@ miel_hvac_publish_settings(struct miel_hvac_softc *sc) miel_hvac_airdirection_map, nitems(miel_hvac_airdirection_map)); if (name != NULL) { ResponseAppend_P(PSTR(",\"AirDirection\":\"%s\""), - name_swing_h == "isee" ? name : "OFF"); + name_swing_h == "auto" ? name : "OFF"); } name = miel_hvac_map_byval(set->prohibit, @@ -1236,8 +1262,13 @@ miel_hvac_sensor(struct miel_hvac_softc *sc) const char *name_swing_h; ResponseAppend_P(PSTR("," "\"MiElHVAC\":{")); - ResponseAppend_P(PSTR("\"Power\":\"%s\""), - set->power ? "ON" : "OFF"); + + name = miel_hvac_map_byval(set->power, + miel_hvac_power_map, nitems(miel_hvac_power_map)); + if (name != NULL) { + ResponseAppend_P(PSTR("\"Power\":\"%s\""), + name); + } name = miel_hvac_map_byval( set->mode & MIEL_HVAC_SETTINGS_MODE_MASK, @@ -1283,7 +1314,7 @@ miel_hvac_sensor(struct miel_hvac_softc *sc) miel_hvac_airdirection_map, nitems(miel_hvac_airdirection_map)); if (name != NULL) { ResponseAppend_P(PSTR(",\"AirDirection\":\"%s\""), - name_swing_h == "isee" ? name : "OFF"); + name_swing_h == "auto" ? name : "off"); } name = miel_hvac_map_byval(set->prohibit, @@ -1317,13 +1348,28 @@ miel_hvac_sensor(struct miel_hvac_softc *sc) } if (sc->sc_status.type != 0) { - const struct miel_hvac_data_status *s = + const struct miel_hvac_data_status *status = &sc->sc_status.data.status; - ResponseAppend_P(PSTR(",\"Operation\":\"%s\""), - s->operation ? "ON" : "OFF"); - ResponseAppend_P(PSTR(",\"Compressor\":\"%s\""), - s->compressor ? "ON" : "OFF"); + name = miel_hvac_map_byval(status->compressor, + miel_hvac_compressor_map, nitems(miel_hvac_compressor_map)); + if (name != NULL) { + ResponseAppend_P(PSTR(",\"Compressor\":\"%s\""), + name); + } + + uint16_t combined_power = ((uint16_t)status->operationpower << 8) | (uint16_t)status->operationpower1; + char operationpower[33]; + dtostrfd((float)combined_power, 0, operationpower); + ResponseAppend_P(PSTR(",\"OperationPower\":\"%s\""), + operationpower); + + uint16_t combined_energy = ((uint16_t)status->operationenergy << 8) | (uint16_t)status->operationenergy1; + float operationenergy_in_kWh = (float)combined_energy / 10.0; + char operationenergy[33]; + dtostrfd((float)operationenergy_in_kWh, 1, operationenergy); + ResponseAppend_P(PSTR(",\"OperationEnergy\":\"%s\""), + operationenergy); } if (sc->sc_temp.type != 0) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino index 3ede93568..f5fd4694b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino @@ -19,6 +19,8 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.1.0.6 20241014 update - Fix received light command loop + - Add send collision detection 0.1.0.5 20241014 update - Add command `DaliSend [repeat]
,` - Add command `DaliQuery [repeat]
,` - Send frame twice (repeat) for DALI defined commands @@ -93,6 +95,7 @@ struct DALI { bool power; bool available; bool response; + bool light_sync; } *Dali = nullptr; /*********************************************************************************************\ @@ -191,19 +194,40 @@ void DaliSendDataOnce(uint16_t send_dali_data) { 1 2 3 */ bool bit_value; + bool pin_value; + bool collision = false; uint32_t bit_pos = 15; uint32_t wait = ESP.getCycleCount(); - for (uint32_t i = 0; i < 35; i++) { // 417 * 35 = 14.7 ms - if (0 == (i &1)) { // Even bit - // Start bit, Stop bit, Data bits - bit_value = (0 == i) ? 1 : (34 == i) ? 0 : (bool)((send_dali_data >> bit_pos--) &1); // MSB first - } else { // Odd bit - bit_value = !bit_value; // Complement bit + uint32_t bit_number = 0; + while (bit_number < 35) { // 417 * 35 = 14.7 ms + if (!collision) { + if (0 == (bit_number &1)) { // Even bit + // Start bit, Stop bit, Data bits + bit_value = (0 == bit_number) ? 1 : (34 == bit_number) ? 0 : (bool)((send_dali_data >> bit_pos--) &1); // MSB first + } else { // Odd bit + bit_value = !bit_value; // Complement bit + } + pin_value = bit_value ? LOW : HIGH; // Invert bit + } else { + if (34 == bit_number) { + pin_value = HIGH; // Set to idle + } } - bool pin_value = bit_value ? LOW : HIGH; // Invert bit + digitalWrite(Dali->pin_tx, (pin_value == DALI_OUT_INVERT) ? LOW : HIGH); wait += Dali->bit_time; // Auto roll-over while (ESP.getCycleCount() < wait); + + if (!collision) { + if ((HIGH == pin_value) && (LOW == digitalRead(Dali->pin_rx))) { // Collision if write is 1 and bus is 0 + collision = true; + pin_value = LOW; + bit_number = 29; // Keep bus low for 4 bits + AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Send collision")); + } + } + + bit_number++; } // delayMicroseconds(1100); // Adds to total 15.8 ms } @@ -293,18 +317,20 @@ void DaliInput(void) { if (DALI_BROADCAST_DP == Dali->address) { uint8_t dimmer_old = changeUIntScale(Dali->dimmer, 0, 254, 0, 100); uint8_t power_old = Dali->power; - Dali->power = (Dali->command); // State + Dali->power = (Dali->command); // State if (Dali->power) { - Dali->dimmer = Dali->command; // Value + Dali->dimmer = Dali->command; // Value } - if (Settings->sbflag1.dali_web) { // DaliWeb 1 + if (Settings->sbflag1.dali_web) { // DaliWeb 1 uint8_t dimmer_new = changeUIntScale(Dali->dimmer, 0, 254, 0, 100); if (power_old != Dali->power) { + Dali->light_sync = true; // Block local loop ExecuteCommandPower(LightDevice(), Dali->power, SRC_SWITCH); } else if (dimmer_old != dimmer_new) { char scmnd[20]; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), dimmer_new); + Dali->light_sync = true; // Block local loop ExecuteCommand(scmnd, SRC_SWITCH); } show_response = false; @@ -316,9 +342,9 @@ void DaliInput(void) { } #else if (DALI_BROADCAST_DP == Dali->address) { - Dali->power = (Dali->command); // State + Dali->power = (Dali->command); // State if (Dali->power) { - Dali->dimmer = Dali->command; // Value + Dali->dimmer = Dali->command; // Value } } ResponseDali(); @@ -328,6 +354,21 @@ void DaliInput(void) { Dali->available = false; } +#ifdef USE_LIGHT +bool DaliSetChannels(void) { + if (Settings->sbflag1.dali_web) { // DaliWeb 1 + if (Dali->light_sync) { // Block local loop + Dali->light_sync = false; + } else { + uint8_t value = ((uint8_t*)XdrvMailbox.data)[0]; + if (255 == value) { value = 254; } // Max Dali value + DaliPower(value); + } + } + return true; +} +#endif // USE_LIGHT + bool DaliInit(void) { if (!PinUsed(GPIO_DALI_TX) || !PinUsed(GPIO_DALI_RX)) { return false; } @@ -366,17 +407,6 @@ bool DaliInit(void) { #endif // USE_LIGHT } -#ifdef USE_LIGHT -bool DaliSetChannels(void) { - if (Settings->sbflag1.dali_web) { // DaliWeb 1 - uint8_t value = ((uint8_t*)XdrvMailbox.data)[0]; - if (255 == value) { value = 254; } // Max Dali value - DaliPower(value); - } - return true; -} -#endif // USE_LIGHT - /*********************************************************************************************\ * Experimental - Not functioning \*********************************************************************************************/