From 92a54f7ae594c8c40eb4577bf8ca9cc21469dd2d Mon Sep 17 00:00:00 2001 From: root Date: Sat, 15 Feb 2025 17:16:41 +1300 Subject: [PATCH] various incl esphome devices --- .../ts0601_power_din_tuya.cpython-313.pyc | Bin 0 -> 19019 bytes .../ts0601_smoke_heimann.cpython-313.pyc | Bin 0 -> 5094 bytes .../ts0601_smoke_heimann2.cpython-313.pyc | Bin 0 -> 5095 bytes .../ts0601_smoke_tuya.cpython-313.pyc | Bin 0 -> 5091 bytes custom_zha_quirks/ts0601_power_din_tuya.py | 471 +++++++++++++ .../ts0601_power_din_tuya.py.old | 293 ++++++++ custom_zha_quirks/ts0601_smoke_tuya.py | 120 ++++ esphome/esp-athombulbflasher.yaml | 244 +++++++ esphome/esp-leafbat.yaml | 2 +- esphome/esp-masterbathtowelrail.yaml | 629 ++++++++++++++++++ esphome/trash/esp-entmulti.yaml | 488 -------------- ..._practice.yaml => piano_practice.yaml.old} | 0 12 files changed, 1758 insertions(+), 489 deletions(-) create mode 100644 custom_zha_quirks/__pycache__/ts0601_power_din_tuya.cpython-313.pyc create mode 100644 custom_zha_quirks/__pycache__/ts0601_smoke_heimann.cpython-313.pyc create mode 100644 custom_zha_quirks/__pycache__/ts0601_smoke_heimann2.cpython-313.pyc create mode 100644 custom_zha_quirks/__pycache__/ts0601_smoke_tuya.cpython-313.pyc create mode 100644 custom_zha_quirks/ts0601_power_din_tuya.py create mode 100644 custom_zha_quirks/ts0601_power_din_tuya.py.old create mode 100644 custom_zha_quirks/ts0601_smoke_tuya.py create mode 100644 esphome/esp-athombulbflasher.yaml create mode 100644 esphome/esp-masterbathtowelrail.yaml delete mode 100644 esphome/trash/esp-entmulti.yaml rename packages/{piano_practice.yaml => piano_practice.yaml.old} (100%) diff --git a/custom_zha_quirks/__pycache__/ts0601_power_din_tuya.cpython-313.pyc b/custom_zha_quirks/__pycache__/ts0601_power_din_tuya.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6b71dcb06475e71e823189a9b85aa6fd43dea94 GIT binary patch literal 19019 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 Vckm&B!Nc*Bg6+=QBLW@Q>0fshGlT#D literal 0 HcmV?d00001 diff --git a/custom_zha_quirks/__pycache__/ts0601_smoke_heimann2.cpython-313.pyc b/custom_zha_quirks/__pycache__/ts0601_smoke_heimann2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c281141d7bcf172ea7a61f559646436e5836cc24 GIT binary patch literal 5095 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 Wc5mSF>O*Xdt(1T&og literal 0 HcmV?d00001 diff --git a/custom_zha_quirks/__pycache__/ts0601_smoke_tuya.cpython-313.pyc b/custom_zha_quirks/__pycache__/ts0601_smoke_tuya.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96ae9bc5659f75d7c8fb12c307b22b7b3cd4d6dd GIT binary patch literal 5091 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 SgGb{h1l#S^#{@dA)4u>oAu>t; literal 0 HcmV?d00001 diff --git a/custom_zha_quirks/ts0601_power_din_tuya.py b/custom_zha_quirks/ts0601_power_din_tuya.py new file mode 100644 index 0000000..eb01e47 --- /dev/null +++ b/custom_zha_quirks/ts0601_power_din_tuya.py @@ -0,0 +1,471 @@ +"""Tuya Din Power Meter.""" +import logging +from zigpy.profiles import zha +import zigpy.types as t +from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time +from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement +from zigpy.zcl.clusters.smartenergy import Metering + +from zhaquirks import Bus, LocalDataCluster +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, +) +from zhaquirks.tuya import TuyaManufClusterAttributes, TuyaOnOff, TuyaSwitch + +TUYA_TOTAL_ENERGY_ATTR = 0x0211 #energy +TUYA_CURRENT_ATTR = 0x0212 #current +TUYA_POWER_ATTR = 0x0213 #power +TUYA_VOLTAGE_ATTR = 0x0214 #voltaje +TUYA_DIN_SWITCH_ATTR = 0x0101 #switch + +SWITCH_EVENT = "switch_event" + +"""Hiking Power Meter Attributes""" +HIKING_DIN_SWITCH_ATTR = 0x0110 +HIKING_TOTAL_ENERGY_DELIVERED_ATTR = 0x0201 +HIKING_TOTAL_ENERGY_RECEIVED_ATTR = 0x0266 +HIKING_VOLTAGE_CURRENT_ATTR = 0x0006 +HIKING_POWER_ATTR = 0x0267 +HIKING_FREQUENCY_ATTR = 0x0269 +HIKING_POWER_FACTOR_ATTR = 0x026F +HIKING_TOTAL_REACTIVE_ATTR = 0x026D +HIKING_REACTIVE_POWER_ATTR = 0x026E + +"""MatSee Power Meter Attributes""" +MATSEE_DPID_POWER_TOTAL_ID_ATTR = 0x0273 +MATSEE_DPID_POWER_ID_A_ATTR = 0x0265 +MATSEE_DPID_POWER_ID_B_ATTR = 0x0269 +MATSEE_DPID_POWER_DIRECTION_ID_A_ATTR = 0x0266 +MATSEE_DPID_POWER_DIRECTION_ID_B_ATTR = 0x0268 +MATSEE_DPID_FORWARD_ENERGY_TOTAL_A_ATTR = 0x026A +MATSEE_DPID_REVERSE_ENERGY_TOTAL_A_ATTR = 0x026B +MATSEE_DPID_FORWARD_ENERGY_TOTAL_B_ATTR = 0x026C +MATSEE_DPID_REVERSE_ENERGY_TOTAL_B_ATTR = 0x026D +MATSEE_DPID_POWER_FACTOR_A_ATTR = 0x026E +MATSEE_DPID_POWER_FACTOR_B_ATTR = 0x0279 +MATSEE_DPID_POWER_FREQ_ATTR = 0x026F +MATSEE_DPID_VOLTAGE_A_ATTR = 0x0270 +MATSEE_DPID_CURRENT_A_ATTR = 0x0271 +MATSEE_DPID_CURRENT_B_ATTR = 0x0272 +MATSEE_DPID_UPDATE_RATE_ATTR = 0x0281 +MATSEE_DPID_VOLTAGE_A_COEF_ATTR = 0x0274 +MATSEE_DPID_CURRENT_A_COEF_ATTR = 0x0275 +MATSEE_DPID_POWER_A_COEF_ATTR = 0x0276 +MATSEE_DPID_ENERGY_A_COEF_ATTR = 0x0277 +MATSEE_DPID_ENERGY_A_COEF_REV_ATTR = 0x027F +MATSEE_DPID_FREQ_COEF_ATTR = 0x027A +MATSEE_DPID_CURRENT_B_COEF_ATTR = 0x027B +MATSEE_DPID_POWER_B_COEF_ATTR = 0x027C +MATSEE_DPID_ENERGY_B_COEF_ATTR = 0x027D +MATSEE_DPID_ENERGY_B_COEF_REV_ATTR = 0x0280 + +_LOGGER = logging.getLogger(__name__) + +class TuyaManufClusterDinPower(TuyaManufClusterAttributes): + """Manufacturer Specific Cluster of the Tuya Power Meter device.""" + + attributes = { + TUYA_TOTAL_ENERGY_ATTR: ("energy", t.uint32_t, True), + TUYA_CURRENT_ATTR: ("current", t.int16s, True), + TUYA_POWER_ATTR: ("power", t.uint16_t, True), + TUYA_VOLTAGE_ATTR: ("voltage", t.uint16_t, True), + TUYA_DIN_SWITCH_ATTR: ("switch", t.uint8_t, True), + } + + def _update_attribute(self, attrid, value): + super()._update_attribute(attrid, value) + if attrid == TUYA_TOTAL_ENERGY_ATTR: + self.endpoint.smartenergy_metering.energy_deliver_reported(value / 100) + elif attrid == TUYA_CURRENT_ATTR: + self.endpoint.electrical_measurement.current_reported(value) + elif attrid == TUYA_POWER_ATTR: + self.endpoint.electrical_measurement.power_reported(value / 10) + elif attrid == TUYA_VOLTAGE_ATTR: + self.endpoint.electrical_measurement.voltage_reported(value / 10) + elif attrid == TUYA_DIN_SWITCH_ATTR: + self.endpoint.device.switch_bus.listener_event( + SWITCH_EVENT, self.endpoint.endpoint_id, value + ) + + +class TuyaPowerMeasurement(LocalDataCluster, ElectricalMeasurement): + """Custom class for power, voltage and current measurement.""" + + cluster_id = ElectricalMeasurement.cluster_id + + POWER_ID = 0x050B + VOLTAGE_ID = 0x0505 + CURRENT_ID = 0x0508 + REACTIVE_POWER_ID = 0x050E + AC_FREQUENCY_ID = 0x0300 + TOTAL_REACTIVE_POWER_ID = 0x0305 + POWER_FACTOR_ID = 0x0510 + + AC_VOLTAGE_MULTIPLIER = 0x0600 + AC_VOLTAGE_DIVISOR = 0x0601 + AC_CURRENT_MULTIPLIER = 0x0602 + AC_CURRENT_DIVISOR = 0x0603 + AC_FREQUENCY_MULTIPLIER = 0x0400 + AC_FREQUENCY_DIVISOR = 0x0401 + + _CONSTANT_ATTRIBUTES = { + AC_VOLTAGE_MULTIPLIER: 1, + AC_VOLTAGE_DIVISOR: 10, + AC_CURRENT_MULTIPLIER: 1, + AC_CURRENT_DIVISOR: 1000, + AC_FREQUENCY_MULTIPLIER: 1, + AC_FREQUENCY_DIVISOR: 100, + } + + def voltage_reported(self, value): + """Voltage reported.""" + self._update_attribute(self.VOLTAGE_ID, value) + + def power_reported(self, value): + """Power reported.""" + self._update_attribute(self.POWER_ID, value) + + def power_factor_reported(self, value): + """Power Factor reported.""" + self._update_attribute(self.POWER_FACTOR_ID, value) + + def reactive_power_reported(self, value): + """Reactive Power reported.""" + self._update_attribute(self.REACTIVE_POWER_ID, value) + + def current_reported(self, value): + """Ampers reported.""" + self._update_attribute(self.CURRENT_ID, value) + + def frequency_reported(self, value): + """AC Frequency reported.""" + self._update_attribute(self.AC_FREQUENCY_ID, value) + + def reactive_energy_reported(self, value): + """Summation Reactive Energy reported.""" + self._update_attribute(self.TOTAL_REACTIVE_POWER_ID, value) + + +class TuyaElectricalMeasurement(LocalDataCluster, Metering): + """Custom class for total energy measurement.""" + + cluster_id = Metering.cluster_id + CURRENT_DELIVERED_ID = 0x0000 + CURRENT_RECEIVED_ID = 0x0001 + #CURRENT_TIER1_DELIVERED_ID = 0x0100 + POWER_WATT = 0x0000 + + """Setting unit of measurement.""" + _CONSTANT_ATTRIBUTES = {0x0300: POWER_WATT} + + def energy_deliver_reported(self, value): + """Summation Energy Deliver reported.""" + self._update_attribute(self.CURRENT_DELIVERED_ID, value) + + def energy_receive_reported(self, value): + """Summation Energy Receive reported.""" + self._update_attribute(self.CURRENT_RECEIVED_ID, value) + + #def energy_tier1_deliver_reported(self, value): + # """Summation Energy Tier1 Receive reported.""" + # self._update_attribute(self.CURRENT_TIER1_DELIVERED_ID, value) + +class HikingManufClusterDinPower(TuyaManufClusterAttributes): + """Manufacturer Specific Cluster of the Hiking Power Meter device.""" + + attributes = { + HIKING_DIN_SWITCH_ATTR: ("switch", t.uint8_t, True), + HIKING_TOTAL_ENERGY_DELIVERED_ATTR: ("energy_delivered", t.uint32_t, True), + HIKING_TOTAL_ENERGY_RECEIVED_ATTR: ("energy_received", t.uint16_t, True), + HIKING_VOLTAGE_CURRENT_ATTR: ("voltage_current", t.uint32_t, True), + HIKING_POWER_ATTR: ("power", t.uint16_t, True), + HIKING_FREQUENCY_ATTR: ("frequency", t.uint16_t, True), + HIKING_TOTAL_REACTIVE_ATTR: ("total_reactive_energy", t.int32s, True), + HIKING_REACTIVE_POWER_ATTR: ("reactive_power", t.int16s, True), + HIKING_POWER_FACTOR_ATTR: ("power_factor", t.uint16_t, True), + } + + def _update_attribute(self, attrid, value): + super()._update_attribute(attrid, value) + if attrid == HIKING_DIN_SWITCH_ATTR: + self.endpoint.device.switch_bus.listener_event(SWITCH_EVENT, 16, value) + elif attrid == HIKING_TOTAL_ENERGY_DELIVERED_ATTR: + self.endpoint.smartenergy_metering.energy_deliver_reported(value / 100) + elif attrid == HIKING_TOTAL_ENERGY_RECEIVED_ATTR: + self.endpoint.smartenergy_metering.energy_receive_reported(value / 100) + elif attrid == HIKING_VOLTAGE_CURRENT_ATTR: + self.endpoint.electrical_measurement.current_reported(value >> 16) + self.endpoint.electrical_measurement.voltage_reported( + (value & 0x0000FFFF) / 10 + ) + elif attrid == HIKING_POWER_ATTR: + self.endpoint.electrical_measurement.power_reported(value) + elif attrid == HIKING_FREQUENCY_ATTR: + self.endpoint.electrical_measurement.frequency_reported(value) + elif attrid == HIKING_TOTAL_REACTIVE_ATTR: + self.endpoint.electrical_measurement.reactive_energy_reported(value) + elif attrid == HIKING_REACTIVE_POWER_ATTR: + self.endpoint.electrical_measurement.reactive_power_reported(value) + elif attrid == HIKING_POWER_FACTOR_ATTR: + self.endpoint.electrical_measurement.power_factor_reported(value / 10) + +class MatSeeManufClusterDinPower(TuyaManufClusterAttributes): + """Manufacturer Specific Cluster of the Hiking Power Meter device.""" + _LOGGER.debug("PRUEBA MatSeeManufClusterDinPower") + + attributes = { + HIKING_DIN_SWITCH_ATTR: ("switch", t.uint8_t, True), + MATSEE_DPID_REVERSE_ENERGY_TOTAL_A_ATTR: ("energy_delivered", t.uint32_t, True), + MATSEE_DPID_FORWARD_ENERGY_TOTAL_A_ATTR: ("energy_received", t.uint32_t, True), + MATSEE_DPID_REVERSE_ENERGY_TOTAL_B_ATTR: ("energy_delivered_b", t.uint32_t, True), + MATSEE_DPID_FORWARD_ENERGY_TOTAL_B_ATTR: ("energy_received_b", t.uint32_t, True), + MATSEE_DPID_VOLTAGE_A_ATTR: ("voltage_current", t.uint32_t, True), + MATSEE_DPID_POWER_ID_A_ATTR: ("power", t.uint32_t, True), + MATSEE_DPID_POWER_ID_B_ATTR: ("power_b", t.uint32_t, True), + MATSEE_DPID_POWER_FREQ_ATTR: ("frequency", t.uint32_t, True), + MATSEE_DPID_POWER_TOTAL_ID_ATTR: ("total_reactive_energy", t.int32s, True), + MATSEE_DPID_POWER_A_COEF_ATTR: ("reactive_power", t.uint32_t, True), + MATSEE_DPID_POWER_B_COEF_ATTR: ("reactive_power_b", t.uint32_t, True), + MATSEE_DPID_POWER_FACTOR_A_ATTR: ("power_factor", t.uint32_t, True), + MATSEE_DPID_POWER_FACTOR_B_ATTR: ("power_factor_b", t.uint32_t, True), + MATSEE_DPID_CURRENT_A_ATTR: ("current", t.uint32_t, True), + MATSEE_DPID_CURRENT_B_ATTR: ("current_b", t.uint32_t, True), + MATSEE_DPID_POWER_DIRECTION_ID_A_ATTR: ("power_direction", t.uint8_t, True), + MATSEE_DPID_POWER_DIRECTION_ID_B_ATTR: ("power_direction_b", t.uint8_t, True), + MATSEE_DPID_UPDATE_RATE_ATTR: ("update_rate", t.uint32_t, True), + MATSEE_DPID_VOLTAGE_A_COEF_ATTR: ("voltage_coef", t.uint32_t, True), + MATSEE_DPID_CURRENT_A_COEF_ATTR: ("current_coef", t.uint32_t, True), + MATSEE_DPID_CURRENT_B_COEF_ATTR: ("current_coef_b", t.uint32_t, True), + MATSEE_DPID_ENERGY_A_COEF_ATTR: ("energy_coef", t.uint32_t, True), + MATSEE_DPID_ENERGY_B_COEF_ATTR: ("energy_coef_b", t.uint32_t, True), + MATSEE_DPID_ENERGY_A_COEF_REV_ATTR: ("energy_coef_rev", t.uint32_t, True), + MATSEE_DPID_ENERGY_B_COEF_REV_ATTR: ("energy_coef_rev_b", t.uint32_t, True), + MATSEE_DPID_FREQ_COEF_ATTR: ("frequency_coef", t.uint32_t, True), + } + + def _update_attribute(self, attrid, value): + super()._update_attribute(attrid, value) + if attrid == HIKING_DIN_SWITCH_ATTR: + self.endpoint.device.switch_bus.listener_event(SWITCH_EVENT, 16, value) + elif attrid == MATSEE_DPID_REVERSE_ENERGY_TOTAL_A_ATTR: + self.endpoint.smartenergy_metering.energy_deliver_reported(value / 100) + elif attrid == MATSEE_DPID_FORWARD_ENERGY_TOTAL_A_ATTR: + self.endpoint.smartenergy_metering.energy_receive_reported(value / 100) + #elif attrid == MATSEE_DPID_REVERSE_ENERGY_TOTAL_B_ATTR: + # self.endpoint.smartenergy_metering.energy_tier1_deliver_reported(value / 100) + elif attrid == MATSEE_DPID_CURRENT_A_ATTR: + self.endpoint.electrical_measurement.current_reported(value) + #elif attrid == MATSEE_DPID_CURRENT_B_ATTR: + # self.endpoint.electrical_measurement.current_reported((value & 0x0000FFFF)/1000) + elif attrid == MATSEE_DPID_VOLTAGE_A_ATTR: + #self.endpoint.electrical_measurement.current_reported(value >> 16) + #self.endpoint.electrical_measurement.current_reported((value & 0x0000FFFF) / 10) + self.endpoint.electrical_measurement.voltage_reported( + value + ) + elif attrid == MATSEE_DPID_POWER_ID_A_ATTR: + self.endpoint.electrical_measurement.power_reported(value) + elif attrid == MATSEE_DPID_POWER_FREQ_ATTR: + self.endpoint.electrical_measurement.frequency_reported(value) + elif attrid == MATSEE_DPID_POWER_TOTAL_ID_ATTR: + self.endpoint.electrical_measurement.reactive_energy_reported(value) + elif attrid == MATSEE_DPID_POWER_A_COEF_ATTR: + self.endpoint.electrical_measurement.reactive_power_reported(value) + elif attrid == MATSEE_DPID_POWER_FACTOR_A_ATTR: + self.endpoint.electrical_measurement.power_factor_reported(value / 100) + + +class TuyaPowerMeter(TuyaSwitch): + """Tuya power meter device.""" + + def __init__(self, *args, **kwargs): + """Init device.""" + self.switch_bus = Bus() + super().__init__(*args, **kwargs) + + signature = { + # "node_descriptor": "", + # device_version=1 + # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] + # output_clusters=[0x000a, 0x0019] + MODELS_INFO: [ + ("_TZE200_byzdayie", "TS0601"), + ("_TZE200_ewxhg6o9", "TS0601"), + ("_TZE204_81yrt3lo", "TS0601"), + ], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterAttributes.cluster_id, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterDinPower, + TuyaPowerMeasurement, + TuyaElectricalMeasurement, + TuyaOnOff, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + } + } + + +class HikingPowerMeter(TuyaSwitch): + """Hiking Power Meter Device - DDS238-2.""" + + signature = { + # "node_descriptor": "", + # device_version=1 + # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] + # output_clusters=[0x000a, 0x0019] + MODELS_INFO: [("_TZE200_bkkmqmyo", "TS0601")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterAttributes.cluster_id, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + HikingManufClusterDinPower, + TuyaElectricalMeasurement, + TuyaPowerMeasurement, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + }, + 16: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + TuyaOnOff, + ], + OUTPUT_CLUSTERS: [], + }, + } + } + +class TuyaPowerMeter_GPP(TuyaSwitch): + """Tuya power meter device.""" + + def __init__(self, *args, **kwargs): + """Init device.""" + self.switch_bus = Bus() + super().__init__(*args, **kwargs) + + signature = { + # "node_descriptor": "", + # device_version=1 + # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] + # output_clusters=[0x000a, 0x0019] + MODELS_INFO: [ + ("_TZE200_lsanae15", "TS0601"), + ("_TZE204_81yrt3lo", "TS0601"), + ], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterAttributes.cluster_id, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + }, + 242: { + # > 16) + self.endpoint.electrical_measurement.voltage_reported( + (value & 0x0000FFFF) / 10 + ) + elif attrid == HIKING_POWER_ATTR: + self.endpoint.electrical_measurement.power_reported(value) + elif attrid == HIKING_FREQUENCY_ATTR: + self.endpoint.electrical_measurement.frequency_reported(value) + elif attrid == HIKING_TOTAL_REACTIVE_ATTR: + self.endpoint.electrical_measurement.reactive_energy_reported(value) + elif attrid == HIKING_REACTIVE_POWER_ATTR: + self.endpoint.electrical_measurement.reactive_power_reported(value) + elif attrid == HIKING_POWER_FACTOR_ATTR: + self.endpoint.electrical_measurement.power_factor_reported(value / 10) + + +class TuyaPowerMeter(TuyaSwitch): + """Tuya power meter device.""" + + def __init__(self, *args, **kwargs): + """Init device.""" + self.switch_bus = Bus() + super().__init__(*args, **kwargs) + + signature = { + # "node_descriptor": "", + # device_version=1 + # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] + # output_clusters=[0x000a, 0x0019] + MODELS_INFO: [ + ("_TZE200_byzdayie", "TS0601"), + ("_TZE200_ewxhg6o9", "TS0601"), + ], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterAttributes.cluster_id, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterDinPower, + TuyaPowerMeasurement, + TuyaElectricalMeasurement, + TuyaOnOff, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + } + } + + +class HikingPowerMeter(TuyaSwitch): + """Hiking Power Meter Device - DDS238-2.""" + + signature = { + # "node_descriptor": "", + # device_version=1 + # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] + # output_clusters=[0x000a, 0x0019] + MODELS_INFO: [ + ("_TZE200_bkkmqmyo", "TS0601"), + ("_TZE204_81yrt3lo", "TS0601"), + ], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufClusterAttributes.cluster_id, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + } + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + HikingManufClusterDinPower, + TuyaElectricalMeasurement, + TuyaPowerMeasurement, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + }, + 16: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + TuyaOnOff, + ], + OUTPUT_CLUSTERS: [], + }, + } + } \ No newline at end of file diff --git a/custom_zha_quirks/ts0601_smoke_tuya.py b/custom_zha_quirks/ts0601_smoke_tuya.py new file mode 100644 index 0000000..526c982 --- /dev/null +++ b/custom_zha_quirks/ts0601_smoke_tuya.py @@ -0,0 +1,120 @@ +"""Smoke Sensor.""" +import logging + +import zigpy.profiles.zha +from zigpy.quirks import CustomCluster, CustomDevice +import zigpy.types as t +from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time +from zigpy.zcl.clusters.security import IasZone + +from zhaquirks import Bus +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + ZONE_STATUS, + ZONE_TYPE, +) +from zhaquirks.tuya import TuyaManufCluster, TuyaManufClusterAttributes + +_LOGGER = logging.getLogger(__name__) + +TUYA_SMOKE_DETECTED_ATTR = 0x0401 # [0]/[1] [Detected]/[Clear]! + + +class TuyaSmokeDetectorCluster(TuyaManufClusterAttributes): + """Manufacturer Specific Cluster of the TS0601 smoke detector.""" + + attributes = { + TUYA_SMOKE_DETECTED_ATTR: ("smoke_detected", t.uint8_t, True), + } + + def _update_attribute(self, attrid, value): + super()._update_attribute(attrid, value) + if attrid == TUYA_SMOKE_DETECTED_ATTR: + if value == 0: + self.endpoint.device.ias_bus.listener_event( + "update_zone_status", IasZone.ZoneStatus.Alarm_1 + ) + else: + self.endpoint.device.ias_bus.listener_event("update_zone_status", 0) + else: + _LOGGER.warning( + "[0x%04x:%s:0x%04x] unhandled attribute: 0x%04x", + self.endpoint.device.nwk, + self.endpoint.endpoint_id, + self.cluster_id, + attrid, + ) + + +class TuyaIasZone(CustomCluster, IasZone): + """IAS Zone.""" + + _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Fire_Sensor} + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + self.endpoint.device.ias_bus.add_listener(self) + + def update_zone_status(self, value): + """Update IAS status.""" + super()._update_attribute(ZONE_STATUS, value) + + +class TuyaSmokeDetector0601(CustomDevice): + """TS0601 Smoke detector quirk.""" + + def __init__(self, *args, **kwargs): + """Init.""" + self.ias_bus = Bus() + super().__init__(*args, **kwargs) + + signature = { + MODELS_INFO: [ + ("_TZE200_aycxwiau", "TS0601"), + ("_TZE200_ntcy3xu1", "TS0601"), + ("_TZE200_vzekyi4c", "TS0601"), + ("_TZE200_uebojraa", "TS0601"), + ], + ENDPOINTS: { + 1: { + PROFILE_ID: zigpy.profiles.zha.PROFILE_ID, + DEVICE_TYPE: zigpy.profiles.zha.DeviceType.SMART_PLUG, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaManufCluster.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zigpy.profiles.zha.PROFILE_ID, + DEVICE_TYPE: zigpy.profiles.zha.DeviceType.IAS_ZONE, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + TuyaIasZone, + TuyaSmokeDetectorCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + }, + } \ No newline at end of file diff --git a/esphome/esp-athombulbflasher.yaml b/esphome/esp-athombulbflasher.yaml new file mode 100644 index 0000000..c756adb --- /dev/null +++ b/esphome/esp-athombulbflasher.yaml @@ -0,0 +1,244 @@ +############################################ +############################################# +# ATHOM 15W RGBWW Bulb Flasher +# +# # V1.0 2025-02-15 Initial Version +# +# Device https://www.athom.tech/blank-1/15w-color-bulb +# +# DESCRIPTION +# Starts up and flashes a RGBWW lightbulb a white colour +# On and off time (duty cycle) is configurable +# Colour Temp (in K) is configurable +# There is no fading between transitions +# +############################################# +############################################# + + +############################################# +# USER VARIABLE SUBSTITUTIONS +# Give the device a useful name & description here +# and change values accordingly. +############################################# +substitutions: + devicename: esp-athombulbflasher + friendly_name: "Flasher for Athom 15W Bulb" + description_comment: "Athom RGBWW light bulb that will flash on boot and allow colour temp & duty cycle adjustment" + + # Adjust the color temperature here (200 mired is approx 5000k + white_temp: "3000K" + + # Adjust the ON/OFF times for flashing. + # 600ms on, 400ms off => 60% ON, 40% OFF duty cycle + on_time_ms: "500ms" + off_time_ms: "500ms" + + +############################################# +# OTHER VARIABLE SUBSTITUTIONS +# Give the device a useful name & description here +# and change values accordingly. +############################################# + + # If NOT using a secrets file, just replace these with the passwords etc (in quotes) + api_key: !secret esp-athombulbflasher_api_key # unfortunately you can't use substitutions inside secrets names + ota_pass: !secret esp-athombulbflasher_ota_pass # unfortunately you can't use substitutions inside secrets names + wifi_ssid: !secret wifi_ssid + wifi_password: !secret wifi_password + fallback_ap_password: !secret fallback_ap_password + + # Add these if we are giving it a static ip, or remove them in the Wifi section + #static_ip_address: !secret esp-athombulbflasher_static_ip + #static_ip_gateway: !secret esp-athombulbflasher_gateway + #static_ip_subnet: !secret esp-athombulbflasher_subnet + + #mqtt_server: !secret mqtt_server + #mqtt_username: !secret mqtt_username + #mqtt_password: !secret mqtt_password + #mqtt_topic: "esphome" #main topic for the mqtt server, call it what you like + + # Add these if we are using the internal web server (this is pretty processor intensive) + #web_server_username: !secret web_server_username + #web_server_password: !secret web_server_password + + #update_interval: 60s # update time for for general sensors etc + + +############################################# +# ESP Platform and Framework +# https://esphome.io/components/esp32.html +############################################# +esphome: + name: ${devicename} + friendly_name: ${friendly_name} + comment: ${description_comment} # appears on the esphome page in HA + on_boot: + priority: 800 + then: + - light.turn_on: + id: my_light + effect: "Flashing White" + +############################################# +# ESP Platform and Framework +# https://esphome.io/components/esp32.html +############################################# +esp8266: + board: esp01_1m + +############################################# +# ESPHome Logging Enable +# https://esphome.io/components/logger.html +############################################# +logger: + level: INFO # INFO Level suggested, or DEBUG for testing + #baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) + #esp8266_store_log_strings_in_flash: false + #tx_buffer_size: 64 + +############################################# +# Enable the Home Assistant API +# https://esphome.io/components/api.html +############################################# +api: + encryption: + key: ${api_key} + +############################################# +# Enable Over the Air Update Capability +# https://esphome.io/components/ota.html?highlight=ota +############################################# +ota: + - platform: esphome + password: ${ota_pass} + +############################################# +# Safe Mode +# Safe mode will detect boot loops +# https://esphome.io/components/safe_mode +############################################# +safe_mode: + +############################################# +# Wifi Settings +# https://esphome.io/components/wifi.html +# +# Power Save mode (can reduce wifi reliability) +# NONE (least power saving, Default for ESP8266) +# LIGHT (Default for ESP32) +# HIGH (most power saving) +############################################# +wifi: + ssid: ${wifi_ssid} + password: ${wifi_password} + #power_save_mode: LIGHT # https://esphome.io/components/wifi.html#wifi-power-save-mode + #manual_ip: # optional static IP address + #static_ip: ${static_ip_address} + #gateway: ${static_ip_gateway} + #subnet: ${static_ip_subnet} + ap: # Details for fallback hotspot in case wifi connection fails https://esphome.io/components/wifi.html#access-point-mode + ssid: ${devicename} AP + password: ${fallback_ap_password} + ap_timeout: 30min # Time until it brings up fallback AP. default is 1min + +captive_portal: # extra fallback mechanism for when connecting if the configured WiFi fails + + +############################################# +# MQTT Monitoring +# https://esphome.io/components/mqtt.html?highlight=mqtt +# MUST also have api enabled if you enable MQTT +############################################# +#mqtt: + #broker: ${mqtt_server} + #topic_prefix: ${mqtt_topic}/${devicename} + #username: ${mqtt_username} + #password: ${mqtt_password} + ##discovery: True # enable entity discovery (true is default) + ##discover_ip: True # enable device discovery (true is default) + + +############################################# +# Web Portal for display and monitoring +# Turning this off is probably a good idea to save resources. +# https://esphome.io/components/web_server.html +############################################# +#web_server: +# port: 80 +# auth: +# username: ${web_server_username} # probably a good idea to secure it +# password: ${web_server_password} + + + +# --------------------------------------------------------- +# OUTPUTS: 5 x PWM channels for the Athom 15W RGBWW bulb +# --------------------------------------------------------- +output: + - platform: esp8266_pwm + id: output_red + pin: GPIO4 + frequency: 1000 Hz + + - platform: esp8266_pwm + id: output_green + pin: GPIO5 + frequency: 1000 Hz + + - platform: esp8266_pwm + id: output_blue + pin: GPIO12 + frequency: 1000 Hz + + # Typically one channel is cold white... + - platform: esp8266_pwm + id: output_cold_white + pin: GPIO14 + frequency: 1000 Hz + + # ...and one channel is warm white (inverted on GPIO13). + - platform: esp8266_pwm + id: output_warm_white + pin: GPIO13 + frequency: 1000 Hz + inverted: true + +# --------------------------------------------------------- +# LIGHT COMPONENT: RGBWW + a strobe effect for flashing +# --------------------------------------------------------- +light: + - platform: rgbww + name: ${friendly_name} + id: my_light + red: output_red + green: output_green + blue: output_blue + cold_white: output_cold_white + warm_white: output_warm_white + + # Typical white temperature mapping for RGBWW + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + + # Ensure it always powers on (and with our desired effect). + restore_mode: ALWAYS_ON + + # You can omit this or set it to zero for no fade on effect changes. + default_transition_length: 0s + + # The custom strobe effect for flashing white + effects: + - strobe: + name: "Flashing White" + colors: + # Note the Kelvin value is replaced at compile time: + - state: True + brightness: 100% + color_temperature: "${white_temp}" + duration: ${on_time_ms} + - state: False + brightness: 0% + color_temperature: "${white_temp}" + duration: ${off_time_ms} + diff --git a/esphome/esp-leafbat.yaml b/esphome/esp-leafbat.yaml index 6c755a4..e61fcbf 100644 --- a/esphome/esp-leafbat.yaml +++ b/esphome/esp-leafbat.yaml @@ -1,6 +1,6 @@ ############################################# ############################################# -# BYD ATTO3 12V Battery Monitor +# Nissan Leaf 12V Battery Monitor # Monitoring the status of a vehicle 12V battery with # an esp8266 (D1 Mini). It will obviously only # transmit when the vehicle is within wifi range. diff --git a/esphome/esp-masterbathtowelrail.yaml b/esphome/esp-masterbathtowelrail.yaml new file mode 100644 index 0000000..53de751 --- /dev/null +++ b/esphome/esp-masterbathtowelrail.yaml @@ -0,0 +1,629 @@ +############################################# +############################################# +# MASTER BATHROOM HEATED TOWEL RAIL +# Controlled by a Sonoff Basic +# +# V1.0 2025-02-14 Initial Version +# +# INSTRUCTIONS +# - It allows a heated towel rail device to work in a standalone operation +# - On startup, it will turn on for 2 hours then go into timer mode (this allows you to just turn it on to get some heat immediately) +# - The timer has a morning and evening time (but no weekday/weekend setting) +# - Default values are 5am-7am and 9pm-Midnight (as this suits our use case) +# - It uses SNTP for time setting (but obviously only if wifi & networking are working) +# - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) +# - If on a network and there is a MQTT server, you can set the 4 on/off times via MQTT (See below commands) +# - You can set 4 modes ON/OFF/TIMER/STARTUP via MQTT +# - Any new timer times set via MQTT will be remembered though a reboot +# - On a reboot, the device will always turn on for the Startup Duration (STARTUP mode, default 2 hours) +# - TIMER mode will always be switched on after startup mode is complete +# - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 20 seconds (with ~2 secs in between to allow it to boot) +# +# MQTT Commands +# Values will be set in place on the update_interval time, not immediately +# Use 00:00 in 24hr format for time setting. Note there is no weekday/weekend setting +# mqtt_timer_topic/morning-on/06:00 : Time towel rail will go on +# mqtt_timer_topic/morning-off/08:00 : Time towel rail will go off +# mqtt_timer_topic/evening-on/09:00 : Time towel rail will go on +# mqtt_timer_topic/evening-off/00:00 : Time towel rail will go off +# mqtt_timer_topic/operation/ON : Towel rail permanently on +# mqtt_timer_topic/operation/OFF : Towel rail permanently off +# mqtt_timer_topic/operation/TIMER : Towel rail will obey timer settings +# mqtt_timer_topic/operation/STARTUP : Turn on for 2 hours then TIMER (also on startup) +# +############################################# +############################################# + +############################################# +# VARIABLE SUBSTITUTIONS +# Give the device a useful name & description here +# and change values accordingly. +############################################# +substitutions: + + mqtt_timer_topic: "viewroad-commands/masterbath-towelrail" # Topics you will use to change stuff + startup_duration: "120" # Minutes to stay ON in STARTUP mode before reverting to TIMER + timezone: "Pacific/Auckland" # For setting clock with snmp + + devicename: "esp-masterbathtowelrail" + friendly_name: "Master Bathroom Towelrail" + description_comment: "Sonoff Basic controlling ON/OFF/Timer for the Heated Towel Rail in the Master Bathroom" + + # If NOT using a secrets file, just replace these with the passwords etc (in quotes) + api_key: !secret esp-masterbathtowelrail_api_key # unfortunately you can't use substitutions inside secrets names + ota_pass: !secret esp-masterbathtowelrail_ota_pass # unfortunately you can't use substitutions inside secrets names + wifi_ssid: !secret wifi_ssid + wifi_password: !secret wifi_password + fallback_ap_password: !secret fallback_ap_password + + # Add these if we are giving it a static ip, or remove them in the Wifi section + #static_ip_address: !secret esp-occupancyoffice_static_ip + #static_ip_gateway: !secret esp-occupancyoffice_gateway + #static_ip_subnet: !secret esp-occupancyoffice_subnet + + mqtt_server: !secret mqtt_server + mqtt_username: !secret mqtt_username + mqtt_password: !secret mqtt_password + mqtt_topic: "esphome" #main topic for the mqtt server, call it what you like + + # Add these if we are using the internal web server (this is pretty processor intensive) + #web_server_username: !secret web_server_username + #web_server_password: !secret web_server_password + + update_interval: 60s # update time for for general sensors etc + + +############################################# +# ESPHome +# https://esphome.io/components/esphome.html +############################################# +esphome: + name: ${devicename} + friendly_name: ${friendly_name} + comment: ${description_comment} #a ppears on the esphome page in HA + on_boot: + priority: 900 # high priority to run after globals are initialized + then: + - lambda: |- + // 1) Figure out the current time in "seconds from midnight" + // using SNTP if available, otherwise fallback_time * 60. + bool have_sntp = id(sntp_time).now().is_valid(); + int current_time_s = 0; + + if (have_sntp) { + auto now = id(sntp_time).now(); + current_time_s = now.hour * 3600 + now.minute * 60 + now.second; + } else { + // fallback_time is in minutes; convert to seconds + current_time_s = id(fallback_time) * 60; + } + + // 2) Compare with the last boot time + int diff = current_time_s - id(last_boot_time_s); + + // If within 20 seconds, increment boot_count; otherwise reset to 1 + if (diff >= 0 && diff <= 20) { + id(boot_count)++; + } else { + id(boot_count) = 1; + } + + // Update stored last boot time + id(last_boot_time_s) = current_time_s; + + // 3) If we've booted 4+ times in 20s => force ON mode + if (id(boot_count) >= 4) { + id(operation_mode) = 1; // ON + ESP_LOGI("power_cycle", "Detected 4 power cycles in 20s => Forcing ON mode"); + } else { + // Otherwise do your normal startup logic: + id(operation_mode) = 3; // on_boot -> sets operation_mode = 3 (STARTUP) + id(startup_timer) = 0; // and reset startup_timer = 0 (for time sync if no sntp) + ESP_LOGI("power_cycle", "Boot count=%d => STARTUP mode", id(boot_count)); + } + +############################################# +# ESP Platform and Framework +# https://esphome.io/components/esp32.html +############################################# +esp8266: + board: esp01_1m # The original sonoff basic + +############################################# +# ESPHome Logging Enable +# https://esphome.io/components/logger.html +############################################# +logger: + level: INFO #INFO Level suggested, or DEBUG for testing + #baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) + #esp8266_store_log_strings_in_flash: false + #tx_buffer_size: 64 + +############################################# +# Enable the Home Assistant API +# https://esphome.io/components/api.html +############################################# +api: + encryption: + key: ${api_key} + +############################################# +# Enable Over the Air Update Capability +# https://esphome.io/components/ota.html?highlight=ota +############################################# +ota: + - platform: esphome + password: ${ota_pass} + +############################################# +# Safe Mode +# Safe mode will detect boot loops +# https://esphome.io/components/safe_mode +############################################# +safe_mode: + +############################################# +# Wifi Settings +# https://esphome.io/components/wifi.html +# +# Power Save mode (can reduce wifi reliability) +# NONE (least power saving, Default for ESP8266) +# LIGHT (Default for ESP32) +# HIGH (most power saving) +############################################# +wifi: + ssid: ${wifi_ssid} + password: ${wifi_password} + #power_save_mode: LIGHT # https://esphome.io/components/wifi.html#wifi-power-save-mode + #manual_ip: # optional static IP address + #static_ip: ${static_ip_address} + #gateway: ${static_ip_gateway} + #subnet: ${static_ip_subnet} + ap: # Details for fallback hotspot in case wifi connection fails https://esphome.io/components/wifi.html#access-point-mode + ssid: ${devicename} AP + password: ${fallback_ap_password} + ap_timeout: 30min # Time until it brings up fallback AP. default is 1min + +captive_portal: # extra fallback mechanism for when connecting if the configured WiFi fails + +############################################# +# Real time clock time source for ESPHome +# If it's invalid, we fall back to an internal clock +# https://esphome.io/components/time/index.html +# https://esphome.io/components/time/sntp +############################################# +time: + - platform: sntp + id: sntp_time + +############################################# +# MQTT Monitoring +# https://esphome.io/components/mqtt.html?highlight=mqtt +# MUST also have api enabled if you enable MQTT +############################################# +mqtt: + broker: ${mqtt_server} + topic_prefix: ${mqtt_topic}/${devicename} + username: ${mqtt_username} + password: ${mqtt_password} + #discovery: True # enable entity discovery (true is default) + #discover_ip: True # enable device discovery (true is default) + +############################################# +# Global Variables for use in automations etc +# https://esphome.io/guides/automations.html?highlight=globals#global-variables +############################################# +globals: + + # Tracks the time (in seconds from midnight) at the previous boot + - id: last_boot_time_s + type: int + restore_value: true + initial_value: "0" + + # Counts how many consecutive boots have occurred within 10 seconds + - id: boot_count + type: int + restore_value: true + initial_value: "0" + + # Morning On time (minutes from midnight), + # default 05:00 => 300 + - id: morning_on + type: int + restore_value: true + initial_value: "300" + + # Morning Off time (minutes from midnight), + # default 07:00 => 420 + - id: morning_off + type: int + restore_value: true + initial_value: "420" + + # Evening On time (minutes from midnight), + # default 21:00 => 1260 + - id: evening_on + type: int + restore_value: true + initial_value: "1260" + + # Evening Off time (minutes from midnight), + # default 00:00 => 0 => treat as midnight + - id: evening_off + type: int + restore_value: true + initial_value: "0" + + #################################################### + # operation_mode: + # 0 = OFF + # 1 = ON + # 2 = TIMER + # 3 = STARTUP + #################################################### + - id: operation_mode + type: int + restore_value: false + initial_value: "3" + + #################################################### + # fallback_time is used if SNTP is invalid. + # We assume user powers on the device at 12:00 noon + # => 12 * 60 = 720 minutes from midnight. + # Not restored, so it resets each boot. + #################################################### + - id: fallback_time + type: int + restore_value: false + initial_value: "720" + + #################################################### + # startup_timer: counts minutes in STARTUP mode + # After 'startup_duration' minutes, revert to TIMER. + # Not restored, so each boot starts fresh at 0. + #################################################### + - id: startup_timer + type: int + restore_value: false + initial_value: "0" + +############################################# +# Text Sensors +# https://esphome.io/components/text_sensor/index.html +############################################# +text_sensor: + +############################ +# MQTT Subscriptions +############################ + + #################################################### + # Subscribe to the Morning On time, format "HH:MM" + # We check x.size() == 5 and x[2] == ':', + # then parse x.substr(0,2) and x.substr(3,2) + # std::string uses 'substr', not 'substring'. + #################################################### + - platform: mqtt_subscribe + name: "Morning On Time" + id: morning_on_topic + topic: "${mqtt_timer_topic}/morning-on" + internal: True + on_value: + then: + - lambda: |- + // Expect "HH:MM" => total length = 5, with ':' + if (x.size() == 5 && x[2] == ':') { + int hour = atoi(x.substr(0, 2).c_str()); // "HH" + int minute = atoi(x.substr(3, 2).c_str()); // "MM" + id(morning_on) = hour * 60 + minute; + ESP_LOGI("timer","Received new Morning On: %02d:%02d", hour, minute); + } else { + ESP_LOGW("timer","Invalid Morning On format: %s", x.c_str()); + } + + #################################################### + # Morning Off time => "HH:MM" + #################################################### + - platform: mqtt_subscribe + name: "Morning Off Time" + id: morning_off_topic + topic: "${mqtt_timer_topic}/morning-off" + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + if (x.size() == 5 && x[2] == ':') { + int hour = atoi(x.substr(0, 2).c_str()); + int minute = atoi(x.substr(3, 2).c_str()); + id(morning_off) = hour * 60 + minute; + ESP_LOGI("timer","Received new Morning Off: %02d:%02d", hour, minute); + } else { + ESP_LOGW("timer","Invalid Morning Off format: %s", x.c_str()); + } + + #################################################### + # Evening On time => "HH:MM" + #################################################### + - platform: mqtt_subscribe + name: "Evening On Time" + id: evening_on_topic + topic: "${mqtt_timer_topic}/evening-on" + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + if (x.size() == 5 && x[2] == ':') { + int hour = atoi(x.substr(0, 2).c_str()); + int minute = atoi(x.substr(3, 2).c_str()); + id(evening_on) = hour * 60 + minute; + ESP_LOGI("timer","Received new Evening On: %02d:%02d", hour, minute); + } else { + ESP_LOGW("timer","Invalid Evening On format: %s", x.c_str()); + } + + #################################################### + # Evening Off time => "HH:MM" + #################################################### + - platform: mqtt_subscribe + name: "Evening Off Time" + id: evening_off_topic + topic: "${mqtt_timer_topic}/evening-off" + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + if (x.size() == 5 && x[2] == ':') { + int hour = atoi(x.substr(0, 2).c_str()); + int minute = atoi(x.substr(3, 2).c_str()); + id(evening_off) = hour * 60 + minute; + ESP_LOGI("timer","Received new Evening Off: %02d:%02d", hour, minute); + } else { + ESP_LOGW("timer","Invalid Evening Off format: %s", x.c_str()); + } + + #################################################### + # Subscribe to operation mode: + # OFF, ON, TIMER, STARTUP + # We do case-insensitive compare using strcasecmp + # (Requires typically included in ESPHome) + #################################################### + - platform: mqtt_subscribe + name: "Timer Operation Mode" + id: timer_operation_mode_topic + topic: "${mqtt_timer_topic}/operation" + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + /* + * In standard C++ (ESPHome), no 'equalsIgnoreCase()'. + * We use 'strcasecmp' for case-insensitive compare. + * Returns 0 if they match ignoring case. + */ + if (strcasecmp(x.c_str(), "TIMER") == 0) { + id(operation_mode) = 2; + ESP_LOGI("timer","Operation mode set to TIMER"); + } else if (strcasecmp(x.c_str(), "ON") == 0) { + id(operation_mode) = 1; + ESP_LOGI("timer","Operation mode set to ON"); + } else if (strcasecmp(x.c_str(), "OFF") == 0) { + id(operation_mode) = 0; + ESP_LOGI("timer","Operation mode set to OFF"); + } else if (strcasecmp(x.c_str(), "STARTUP") == 0) { + id(operation_mode) = 3; + id(startup_timer) = 0; + ESP_LOGI("timer","Operation mode set to STARTUP"); + } else { + ESP_LOGW("timer","Invalid operation mode: %s", x.c_str()); + } + + ###################################################### + # Expose the current operation mode (OFF, ON, TIMER, STARTUP) + ###################################################### + - platform: template + name: "Operation Mode State" + lambda: |- + // 0=OFF, 1=ON, 2=TIMER, 3=STARTUP + switch (id(operation_mode)) { + case 0: return {"OFF"}; + case 1: return {"ON"}; + case 2: return {"TIMER"}; + case 3: return {"STARTUP"}; + default: return {"UNKNOWN"}; + } + update_interval: ${update_interval} + + ###################################################### + # Expose the "Morning On" time as a text (HH:MM) + ###################################################### + - platform: template + name: "Morning On Time State" + lambda: |- + int hour = id(morning_on) / 60; + int minute = id(morning_on) % 60; + // Increase to 16 for safety + char buff[16]; + snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); + return { std::string(buff) }; + update_interval: ${update_interval} + + ###################################################### + # Expose the "Morning Off" time as a text (HH:MM) + ###################################################### + - platform: template + name: "Morning Off Time State" + lambda: |- + int hour = id(morning_off) / 60; + int minute = id(morning_off) % 60; + // Increase buffer size to 8 just to be safe + // Increase to 16 for safety + char buff[16]; + snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); + return { std::string(buff) }; + update_interval: ${update_interval} + + ###################################################### + # Expose the "Evening On" time as a text (HH:MM) + ###################################################### + - platform: template + name: "Evening On Time State" + lambda: |- + int hour = id(evening_on) / 60; + int minute = id(evening_on) % 60; + // Increase buffer size to 8 just to be safe + // Increase to 16 for safety + char buff[16]; + snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); + return { std::string(buff) }; + update_interval: ${update_interval} + + ###################################################### + # Expose the "Evening Off" time as a text (HH:MM) + ###################################################### + - platform: template + name: "Evening Off Time State" + lambda: |- + int hour = id(evening_off) / 60; + int minute = id(evening_off) % 60; + // Increase buffer size to 8 just to be safe + // Increase to 16 for safety + char buff[16]; + snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); + return { std::string(buff) }; + update_interval: ${update_interval} + + ###################################################### + # ESPHome Info + ###################################################### + - platform: version + name: ${friendly_name} Version + - platform: wifi_info + ip_address: + name: ${friendly_name} IP Address + +############################################# +# General Sensors +# https://esphome.io/components/sensor/index.html +############################################# +sensor: + - platform: uptime # Uptime for this device + name: ${friendly_name} Uptime + update_interval: ${update_interval} + - platform: wifi_signal # Wifi Strength + name: ${friendly_name} Wifi Signal + update_interval: ${update_interval} + +#################################################### +# Relay Switch (Sonoff Basic Relay on GPIO12) +#################################################### +switch: + - platform: gpio + name: "Towel Rail Power" + pin: GPIO12 + id: relay + restore_mode: RESTORE_DEFAULT_OFF + +#################################################### +# Check every minute to decide relay state +#################################################### +interval: + - interval: ${update_interval} + then: + - lambda: |- + // operation_mode: + // 0 = OFF + // 1 = ON + // 2 = TIMER + // 3 = STARTUP + int mode = id(operation_mode); + + ////////////////////////////////////////////////// + // STARTUP MODE: Relay ON for 'startup_duration' + // minutes, then automatically revert to TIMER. + ////////////////////////////////////////////////// + if (mode == 3) { + id(startup_timer)++; + // Compare with the substitution startup_duration + if (id(startup_timer) < (int) ${startup_duration}) { + // Still within the STARTUP period => turn relay on + id(relay).turn_on(); + } else { + // After 'startup_duration' minutes => switch to TIMER + id(operation_mode) = 2; + } + // Skip the rest of the logic + return; + } + + ////////////////////////////////////////////////// + // OFF MODE => always off + ////////////////////////////////////////////////// + if (mode == 0) { + id(relay).turn_off(); + return; + } + + ////////////////////////////////////////////////// + // ON MODE => always on + ////////////////////////////////////////////////// + if (mode == 1) { + id(relay).turn_on(); + return; + } + + ////////////////////////////////////////////////// + // TIMER MODE => follow morning/evening schedule + // using SNTP if valid, else fallback_time + ////////////////////////////////////////////////// + if (mode == 2) { + auto now = id(sntp_time).now(); + bool have_sntp = now.is_valid(); + + int current_mins; + if (!have_sntp) { + // SNTP not available => fallback clock + current_mins = id(fallback_time); + // increment the fallback clock by 1 minute + id(fallback_time) += 1; + // wrap around at 1440 => next day + if (id(fallback_time) >= 1440) { + id(fallback_time) = 0; + } + } else { + // Use real time from SNTP + current_mins = now.hour * 60 + now.minute; + } + + bool should_on = false; + + // If evening_off == 0 => treat as midnight => 1440 + int evening_off_local = id(evening_off); + if (evening_off_local == 0) { + evening_off_local = 1440; + } + + // Check morning window + // Example: morning_on=360 => 06:00, morning_off=480 => 08:00 + // If current_mins in [360..480), should_on = true + if (id(morning_on) < id(morning_off)) { + if (current_mins >= id(morning_on) && current_mins < id(morning_off)) { + should_on = true; + } + } + + // Check evening window + // Example: evening_on=540 => 09:00, evening_off=1440 => midnight + if (id(evening_on) < evening_off_local) { + if (current_mins >= id(evening_on) && current_mins < evening_off_local) { + should_on = true; + } + } + + // Final relay state based on schedule + if (should_on) { + id(relay).turn_on(); + } else { + id(relay).turn_off(); + } + } \ No newline at end of file diff --git a/esphome/trash/esp-entmulti.yaml b/esphome/trash/esp-entmulti.yaml deleted file mode 100644 index 3329c93..0000000 --- a/esphome/trash/esp-entmulti.yaml +++ /dev/null @@ -1,488 +0,0 @@ -############################################# -############################################# -# -# -############################################# -############################################# - -############################################# -# Variable Substitutions -# Give the device a useful name & description here -# and change values accordingly. -############################################# -substitutions: - devicename: "esp-entmulti" - friendly_name: "Outside Entrance Multisensor" - description_comment: "D1 Mini ESP32 outside entranceway with, mmWave presence, PIR and more" - - #if NOT using a secrets file, just replace these with the passwords etc (in quotes) - api_key: !secret esp-entmulti_api_key #unfortunately you can't use substitutions in secrets names - ota_pass: !secret esp-entmulti_ota_pass #unfortunately you can't use substitutions in secrets names - wifi_ssid: !secret wifi_ssid - wifi_password: !secret wifi_password - fallback_ap_password: !secret fallback_ap_password - #Add these if we are giving it a static ip, or remove them in the Wifi section - #static_ip_address: !secret esp-entmulti_static_ip - #static_ip_gateway: !secret esp-entmulti_gateway - #static_ip_subnet: !secret esp-entmulti_subnet - - mqtt_server: !secret mqtt_server - mqtt_username: !secret mqtt_username - mqtt_password: !secret mqtt_password - mqtt_topic: "esphome" #main topic for the mqtt server, call it what you like - - #web_server_username: !secret web_server_username - #web_server_password: !secret web_server_password - - update_time: 30s #update time for for general temp sensors etc - - -############################################# -# ESPHome -# https://esphome.io/components/esphome.html -############################################# -esphome: - name: ${devicename} - friendly_name: ${friendly_name} - comment: ${description_comment} #appears on the esphome page in HA - min_version: 2024.6.0 - - -############################################# -# ESP Platform and Framework -# https://esphome.io/components/esp32.html -############################################# -esp32: - board: esp32dev - framework: - #type: arduino - type: esp-idf #Suggested Use ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. - version: recommended #recommended, latest or dev - -############################################# -# ESPHome external or custom components to use -# https://esphome.io/components/external_components.html -# https://github.com/ssieb/esphome_components/tree/master/components/serial -############################################# -#external_components: -# - source: -# type: git -# url: https://github.com/ssieb/custom_components #Thanks for @ssieb components. -# components: [ serial ] #text_sensor that reads lines for a uart. Also, a sensor that reads single binary values from the uart. - -############################################# -# ESPHome Logging Enable -# https://esphome.io/components/logger.html -############################################# -logger: - level: INFO #INFO Level suggested, or DEBUG for testing - #baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) - #esp8266_store_log_strings_in_flash: false - #tx_buffer_size: 64 - -############################################# -# Enable the Home Assistant API -# https://esphome.io/components/api.html -############################################# -api: - encryption: - key: ${api_key} -# on_client_connected: -# - esp32_ble_tracker.start_scan: -# continuous: true -# on_client_disconnected: -# - esp32_ble_tracker.stop_scan: - -############################################# -# Enable Over the Air Update Capability -# https://esphome.io/components/ota.html?highlight=ota -############################################# -ota: - - platform: esphome - password: ${ota_pass} - -############################################# -# Safe Mode -# Safe mode will detect boot loops -# https://esphome.io/components/safe_mode -############################################# -safe_mode: - -############################################# -# Wifi Settings -# https://esphome.io/components/wifi.html -# -# Power Save mode (can reduce wifi reliability) -# NONE (least power saving, Default for ESP8266) -# LIGHT (Default for ESP32) -# HIGH (most power saving) -############################################# -wifi: - ssid: ${wifi_ssid} - password: ${wifi_password} - #power_save_mode: LIGHT #https://esphome.io/components/wifi.html#wifi-power-save-mode - #manual_ip: #optional static IP address - #static_ip: ${static_ip_address} - #gateway: ${static_ip_gateway} - #subnet: ${static_ip_subnet} - ap: #Details for fallback hotspot in case wifi connection fails https://esphome.io/components/wifi.html#access-point-mode - ssid: $devicename fallback AP - password: !secret fallback_ap_password - ap_timeout: 5min #Time until it brings up fallback AP. default is 1min - -############################################# -# Web Portal for display and monitoring -# Turning this off is probably a good idea to save resources. -# https://esphome.io/components/web_server.html -############################################# -#web_server: -# port: 80 -# auth: -# username: ${web_server_username} #probably a good idea to secure it -# password: ${web_server_password} - -############################################# -# MQTT Monitoring -# https://esphome.io/components/mqtt.html?highlight=mqtt -# MUST also have api enabled if you enable MQTT -############################################# -mqtt: - broker: ${mqtt_server} - topic_prefix: ${mqtt_topic}/${devicename} - username: ${mqtt_username} - password: ${mqtt_password} - -############################################# -# i2c bus -# https://esphome.io/components/i2c.html -# 10, 50, 100, 200, 800 are possible settings -# for frequency, 50kHz is default -############################################# -#i2c: -# sda: GPIO19 -# scl: GPIO21 -# scan: True #look for devices on boot up and report - #frequency: 100kHz - -############################################# -# UART Serial -# hardware on EPS32, but software, and can be glitchy on ESP8266 -# https://esphome.io/components/uart.html -############################################# -#uart: -# id: ld2410_uart -# rx_pin: GPIO16 #For ESP32, you can use any pin, Recommend Use UART_2, Don't use UART_0, It might Cause Boot Fail or System Hang -# tx_pin: GPIO17 #For ESP32, you can use any pin, Recommend Use UART_2, Don't use UART_0, It might Cause Boot Fail or System Hang -# baud_rate: 256000 # default for LD2410 is 25600, 8, 0, NONE -# data_bits: 8 -# stop_bits: 1 -# parity: NONE - -############################################# -# Bluetooth -# https://esphome.io/components/bluetooth_proxy.html -# https://esphome.io/components/esp32_ble_tracker.html -# Remember that this takes a LOT of processing. On the -# ESP32, enable the IDF framework, and disable the -# Web server component. Changing to the IDF framework -# needs to be via cable not OTA to change the -# partition setup. -############################################# -#bluetooth_proxy: -# active: true -# cache_services: true -# -#esp32_ble_tracker: -# scan_parameters: -# active: true -# continuous: false - -############################################# -# Global Variables for use in automations etc -# https://esphome.io/guides/automations.html?highlight=globals#global-variables -############################################# - - -############################################# -# General esp status LED -# https://esphome.io/components/status_led.html -############################################# -status_led: - pin: - number: GPIO2 #ESP32 Onboard LED - ignore_strapping_warning: True #https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins - inverted: false - -############################################# -# Interval Automations -# https://esphome.io/guides/automations.html -############################################# - -############################################# -# LD2410 Sensors -# https://esphome.io/components/sensor/ld2410.html -# https://www.hlktech.net/index.php?id=988 -############################################# -#ld2410: -# uart_id: ld2410_uart - -############################################# -# Number Sensors (custom component) -# refer https://github.com/ssieb/esphome_components/tree/master/components/serial -############################################# -#number: -# - platform: ld2410 -# timeout: -# name: Timeout -# light_threshold: -# name: Light Threshold -# max_move_distance_gate: -# name: Max Move Distance Gate -# max_still_distance_gate: -# name: Max Still Distance Gate -# g0: -# move_threshold: -# name: g0 move threshold -# still_threshold: -# name: g0 still threshold -# g1: -# move_threshold: -# name: g1 move threshold -# still_threshold: -# name: g1 still threshold -# g2: -# move_threshold: -# name: g2 move threshold -# still_threshold: -# name: g2 still threshold -# g3: -# move_threshold: -# name: g3 move threshold -# still_threshold: -# name: g3 still threshold -# g4: -# move_threshold: -# name: g4 move threshold -# still_threshold: -# name: g4 still threshold -# g5: -# move_threshold: -# name: g5 move threshold -# still_threshold: -# name: g5 still threshold -# g6: -# move_threshold: -# name: g6 move threshold -# still_threshold: -# name: g6 still threshold -# g7: -# move_threshold: -# name: g7 move threshold -# still_threshold: -# name: g7 still threshold -# g8: -# move_threshold: -# name: g8 move threshold -# still_threshold: -# name: g8 still threshold - -#The ld2410 select allows you to control your LD2410 Sensor. -#distance_resolution (Optional): Control the gates distance resolution. Can be 0.75m or 0.2m. Defaults to 0.75m. All options from Select. -#baud_rate (Optional): Control the serial port baud rate. Defaults to 256000. Once changed, all sensors will stop working until a fresh install with an updated UART Component configuration. All options from Select. -#light_function (Optional): If set, will affect the OUT pin value, based on light threshold. Can be off, low or above. Defaults to off. All options from Select. -#out_pin_level (Optional): Control OUT pin away value. Can be low or high. Defaults to low. All options from Select. -#ld2410_id (Optional, ID): Manually specify the ID for the LD2410 Sensor component if you are using multiple components. -#select: -# - platform: ld2410 -# distance_resolution: -# name: ${friendly_name} LD2140 Distance Resolution -# baud_rate: -# name: ${friendly_name} LD2140 Baud Rate -# light_function: -# name: ${friendly_name} LD2140 Light Function -# out_pin_level: -# name: ${friendly_name} LD2140 Out Pin Level - -############################################# -# General Sensors -# https://esphome.io/components/sensor/index.html -############################################# -sensor: -# - platform: bme280_i2c -# address: 0x76 -# update_interval: ${update_time} -# temperature: -# name: ${friendly_name} BME280 Temp -# accuracy_decimals: 1 -# oversampling: 2x -# pressure: -# name: ${friendly_name} BME280 Pressure -# oversampling: 2x -# humidity: -# name: ${friendly_name} BME280 Humidity -# accuracy_decimals: 1 -# oversampling: 2x - - ################################ - # WIFI SIGNAL - # Quality of Wifi in dBm - # https://esphome.io/components/sensor/wifi_signal.html - ################################ - - platform: wifi_signal - name: ${friendly_name} WiFi Signal - update_interval: 20s - #retain: true #retain useful if sleeping - - - platform: uptime - name: ${friendly_name} Uptime - update_interval: 20s - - -#The ld2410 sensor values -# - platform: ld2410 -# light: -# name: Light -# moving_distance: -# name : Moving Distance -# still_distance: -# name: Still Distance -# moving_energy: -# name: Move Energy -# still_energy: -# name: Still Energy -# detection_distance: -# name: Detection Distance -# g0: -# move_energy: -# name: g0 move energy -# still_energy: -# name: g0 still energy -# g1: -# move_energy: -# name: g1 move energy -# still_energy: -# name: g1 still energy -# g2: -# move_energy: -# name: g2 move energy -# still_energy: -# name: g2 still energy -# g3: -# move_energy: -# name: g3 move energy -# still_energy: -# name: g3 still energy -# g4: -# move_energy: -# name: g4 move energy -# still_energy: -# name: g4 still energy -# g5: -# move_energy: -# name: g5 move energy -# still_energy: -# name: g5 still energy -# g6: -# move_energy: -# name: g6 move energy -# still_energy: -# name: g6 still energy -# g7: -# move_energy: -# name: g7 move energy -# still_energy: -# name: g7 still energy -# g8: -# move_energy: -# name: g8 move energy -# still_energy: -# name: g8 still energy - -# The ld2410 switch allows you to control your LD2410 Sensor. -#Bluetooth switch is only useful of you have a B or C model -#switch: -# - platform: ld2410 -# engineering_mode: -# name: ${friendly_name} LD2140 Engineering Mode - #bluetooth: - #name: ${friendly_name} LD2140 Control Bluetooth - - -#The ld2410 button allows resetting -#button: -# - platform: ld2410 -# factory_reset: -# name: ${friendly_name} LD2140 Factory reset" -# restart: -# name: ${friendly_name} LD2140 Restart -## query_params: -# name: Query Parameters - -############################################# -# Text Sensors -# refer https://esphome.io/components/text_sensor/index.html -############################################# -#The ld2410 text sensor allows you to get information about your LD2410 Sensor. -#Bluetooth sensor is only useful of you have a B or C model -#text_sensor: -# - platform: ld2410 -# version: -# name: ${friendly_name} LD2140 Firmware Version - #mac_address: - #name: ${friendly_name} LD2140 BT MAC Address - - -############################################# -# Binary Sensors -# https://esphome.io/components/binary_sensor/index.html -############################################# -binary_sensor: - -# - platform: ld2410 -# has_target: -# name: ${friendly_name} Presence -# has_moving_target: -# name: ${friendly_name} Moving Target -# has_still_target: -# name: ${friendly_name} Still Target -# out_pin_presence_status: -# name: ${friendly_name} LD2140 Out Pin Presence Status - - #Standard PIR Sensor - - platform: gpio - pin: - number: GPIO13 - mode: - input: True - pullup: True - inverted: True - filters: - - delayed_on: 200ms - name: ${friendly_name} PIR Sensor - device_class: motion - - #RF Input from Vibration Sensor (Green Bin) - - platform: gpio - pin: - number: GPIO04 - mode: - input: true - pullup: true - inverted: True - filters: - - delayed_on: 20ms - name: ${friendly_name} Green Bin motion - device_class: vibration - - #RF Input from Vibration Sensor (Red Bin) - - platform: gpio - pin: - number: GPIO15 - mode: - input: true - pullup: true - inverted: True - filters: - - delayed_on: 20ms - name: ${friendly_name} Red Bin motion - device_class: vibration \ No newline at end of file diff --git a/packages/piano_practice.yaml b/packages/piano_practice.yaml.old similarity index 100% rename from packages/piano_practice.yaml rename to packages/piano_practice.yaml.old