mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Merge branch 'dev' into jesserockz-2022-199
This commit is contained in:
		@@ -27,7 +27,7 @@ repos:
 | 
				
			|||||||
          - --branch=release
 | 
					          - --branch=release
 | 
				
			||||||
          - --branch=beta
 | 
					          - --branch=beta
 | 
				
			||||||
  - repo: https://github.com/asottile/pyupgrade
 | 
					  - repo: https://github.com/asottile/pyupgrade
 | 
				
			||||||
    rev: v3.3.1
 | 
					    rev: v3.3.2
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: pyupgrade
 | 
					      - id: pyupgrade
 | 
				
			||||||
        args: [--py39-plus]
 | 
					        args: [--py39-plus]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,6 +110,7 @@ esphome/components/honeywellabp/* @RubyBailey
 | 
				
			|||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
					esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
				
			||||||
esphome/components/hte501/* @Stock-M
 | 
					esphome/components/hte501/* @Stock-M
 | 
				
			||||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
					esphome/components/hydreon_rgxx/* @functionpointer
 | 
				
			||||||
 | 
					esphome/components/hyt271/* @Philippe12
 | 
				
			||||||
esphome/components/i2c/* @esphome/core
 | 
					esphome/components/i2c/* @esphome/core
 | 
				
			||||||
esphome/components/i2s_audio/* @jesserockz
 | 
					esphome/components/i2s_audio/* @jesserockz
 | 
				
			||||||
esphome/components/i2s_audio/media_player/* @jesserockz
 | 
					esphome/components/i2s_audio/media_player/* @jesserockz
 | 
				
			||||||
@@ -139,6 +140,7 @@ esphome/components/ltr390/* @sjtrny
 | 
				
			|||||||
esphome/components/matrix_keypad/* @ssieb
 | 
					esphome/components/matrix_keypad/* @ssieb
 | 
				
			||||||
esphome/components/max31865/* @DAVe3283
 | 
					esphome/components/max31865/* @DAVe3283
 | 
				
			||||||
esphome/components/max44009/* @berfenger
 | 
					esphome/components/max44009/* @berfenger
 | 
				
			||||||
 | 
					esphome/components/max6956/* @looping40
 | 
				
			||||||
esphome/components/max7219digit/* @rspaargaren
 | 
					esphome/components/max7219digit/* @rspaargaren
 | 
				
			||||||
esphome/components/max9611/* @mckaymatthew
 | 
					esphome/components/max9611/* @mckaymatthew
 | 
				
			||||||
esphome/components/mcp23008/* @jesserockz
 | 
					esphome/components/mcp23008/* @jesserockz
 | 
				
			||||||
@@ -188,6 +190,7 @@ esphome/components/nfc/* @jesserockz
 | 
				
			|||||||
esphome/components/number/* @esphome/core
 | 
					esphome/components/number/* @esphome/core
 | 
				
			||||||
esphome/components/ota/* @esphome/core
 | 
					esphome/components/ota/* @esphome/core
 | 
				
			||||||
esphome/components/output/* @esphome/core
 | 
					esphome/components/output/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/pca6416a/* @Mat931
 | 
				
			||||||
esphome/components/pca9554/* @hwstar
 | 
					esphome/components/pca9554/* @hwstar
 | 
				
			||||||
esphome/components/pcf85063/* @brogon
 | 
					esphome/components/pcf85063/* @brogon
 | 
				
			||||||
esphome/components/pid/* @OttoWinter
 | 
					esphome/components/pid/* @OttoWinter
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ RUN \
 | 
				
			|||||||
        python3-cryptography=3.3.2-1 \
 | 
					        python3-cryptography=3.3.2-1 \
 | 
				
			||||||
        python3-venv=3.9.2-3 \
 | 
					        python3-venv=3.9.2-3 \
 | 
				
			||||||
        iputils-ping=3:20210202-1 \
 | 
					        iputils-ping=3:20210202-1 \
 | 
				
			||||||
        git=1:2.30.2-1 \
 | 
					        git=1:2.30.2-1+deb11u2 \
 | 
				
			||||||
        curl=7.74.0-1.3+deb11u7 \
 | 
					        curl=7.74.0-1.3+deb11u7 \
 | 
				
			||||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
					        openssh-client=1:8.4p1-5+deb11u1 \
 | 
				
			||||||
    && rm -rf \
 | 
					    && rm -rf \
 | 
				
			||||||
@@ -63,7 +63,7 @@ RUN \
 | 
				
			|||||||
COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
 | 
					COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
 | 
				
			||||||
RUN \
 | 
					RUN \
 | 
				
			||||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
					    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
				
			||||||
    && /platformio_install_deps.py /platformio.ini
 | 
					    && /platformio_install_deps.py /platformio.ini --libraries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ======================= docker-type image =======================
 | 
					# ======================= docker-type image =======================
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -429,15 +429,16 @@ void APIServer::on_shutdown() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#ifdef USE_VOICE_ASSISTANT
 | 
					#ifdef USE_VOICE_ASSISTANT
 | 
				
			||||||
bool APIServer::start_voice_assistant() {
 | 
					bool APIServer::start_voice_assistant() {
 | 
				
			||||||
  bool result = false;
 | 
					 | 
				
			||||||
  for (auto &c : this->clients_) {
 | 
					  for (auto &c : this->clients_) {
 | 
				
			||||||
    result |= c->request_voice_assistant(true);
 | 
					    if (c->request_voice_assistant(true))
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return result;
 | 
					  return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void APIServer::stop_voice_assistant() {
 | 
					void APIServer::stop_voice_assistant() {
 | 
				
			||||||
  for (auto &c : this->clients_) {
 | 
					  for (auto &c : this->clients_) {
 | 
				
			||||||
    c->request_voice_assistant(false);
 | 
					    if (c->request_voice_assistant(false))
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,5 +18,5 @@ async def to_code(config):
 | 
				
			|||||||
        # https://github.com/esphome/AsyncTCP/blob/master/library.json
 | 
					        # https://github.com/esphome/AsyncTCP/blob/master/library.json
 | 
				
			||||||
        cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
 | 
					        cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
 | 
				
			||||||
    elif CORE.is_esp8266:
 | 
					    elif CORE.is_esp8266:
 | 
				
			||||||
        # https://github.com/OttoWinter/ESPAsyncTCP
 | 
					        # https://github.com/esphome/ESPAsyncTCP
 | 
				
			||||||
        cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3")
 | 
					        cg.add_library("esphome/ESPAsyncTCP-esphome", "1.2.3")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,8 +29,35 @@ BLEClientConnectTrigger = ble_client_ns.class_(
 | 
				
			|||||||
BLEClientDisconnectTrigger = ble_client_ns.class_(
 | 
					BLEClientDisconnectTrigger = ble_client_ns.class_(
 | 
				
			||||||
    "BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
 | 
					    "BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					BLEClientPasskeyRequestTrigger = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientPasskeyRequestTrigger", automation.Trigger.template(BLEClientNodeConstRef)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					BLEClientPasskeyNotificationTrigger = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientPasskeyNotificationTrigger",
 | 
				
			||||||
 | 
					    automation.Trigger.template(BLEClientNodeConstRef, cg.uint32),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					BLEClientNumericComparisonRequestTrigger = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientNumericComparisonRequestTrigger",
 | 
				
			||||||
 | 
					    automation.Trigger.template(BLEClientNodeConstRef, cg.uint32),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Actions
 | 
					# Actions
 | 
				
			||||||
BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action)
 | 
					BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action)
 | 
				
			||||||
 | 
					BLEPasskeyReplyAction = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientPasskeyReplyAction", automation.Action
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					BLENumericComparisonReplyAction = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientNumericComparisonReplyAction", automation.Action
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					BLERemoveBondAction = ble_client_ns.class_(
 | 
				
			||||||
 | 
					    "BLEClientRemoveBondAction", automation.Action
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_PASSKEY = "passkey"
 | 
				
			||||||
 | 
					CONF_ACCEPT = "accept"
 | 
				
			||||||
 | 
					CONF_ON_PASSKEY_REQUEST = "on_passkey_request"
 | 
				
			||||||
 | 
					CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification"
 | 
				
			||||||
 | 
					CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
 | 
					# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
 | 
				
			||||||
# enforce this in yaml checks.
 | 
					# enforce this in yaml checks.
 | 
				
			||||||
@@ -56,6 +83,29 @@ CONFIG_SCHEMA = (
 | 
				
			|||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_PASSKEY_REQUEST): automation.validate_automation(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
				
			||||||
 | 
					                        BLEClientPasskeyRequestTrigger
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_PASSKEY_NOTIFICATION): automation.validate_automation(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
				
			||||||
 | 
					                        BLEClientPasskeyNotificationTrigger
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(
 | 
				
			||||||
 | 
					                CONF_ON_NUMERIC_COMPARISON_REQUEST
 | 
				
			||||||
 | 
					            ): automation.validate_automation(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
				
			||||||
 | 
					                        BLEClientNumericComparisonRequestTrigger
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
@@ -85,13 +135,34 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
				
			||||||
 | 
					        cv.Required(CONF_ACCEPT): cv.templatable(cv.boolean),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BLE_PASSKEY_REPLY_ACTION_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
				
			||||||
 | 
					        cv.Required(CONF_PASSKEY): cv.templatable(cv.int_range(min=0, max=999999)),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BLE_REMOVE_BOND_ACTION_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@automation.register_action(
 | 
					@automation.register_action(
 | 
				
			||||||
    "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA
 | 
					    "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
async def ble_write_to_code(config, action_id, template_arg, args):
 | 
					async def ble_write_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
					    parent = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    value = config[CONF_VALUE]
 | 
					    value = config[CONF_VALUE]
 | 
				
			||||||
    if cg.is_template(value):
 | 
					    if cg.is_template(value):
 | 
				
			||||||
@@ -137,6 +208,54 @@ async def ble_write_to_code(config, action_id, template_arg, args):
 | 
				
			|||||||
    return var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "ble_client.numeric_comparison_reply",
 | 
				
			||||||
 | 
					    BLENumericComparisonReplyAction,
 | 
				
			||||||
 | 
					    BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def numeric_comparison_reply_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    accept = config[CONF_ACCEPT]
 | 
				
			||||||
 | 
					    if cg.is_template(accept):
 | 
				
			||||||
 | 
					        templ = await cg.templatable(accept, args, cg.bool_)
 | 
				
			||||||
 | 
					        cg.add(var.set_value_template(templ))
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        cg.add(var.set_value_simple(accept))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "ble_client.passkey_reply", BLEPasskeyReplyAction, BLE_PASSKEY_REPLY_ACTION_SCHEMA
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def passkey_reply_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passkey = config[CONF_PASSKEY]
 | 
				
			||||||
 | 
					    if cg.is_template(passkey):
 | 
				
			||||||
 | 
					        templ = await cg.templatable(passkey, args, cg.uint32)
 | 
				
			||||||
 | 
					        cg.add(var.set_value_template(templ))
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        cg.add(var.set_value_simple(passkey))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "ble_client.remove_bond",
 | 
				
			||||||
 | 
					    BLERemoveBondAction,
 | 
				
			||||||
 | 
					    BLE_REMOVE_BOND_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def remove_bond_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    await cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
@@ -148,3 +267,12 @@ async def to_code(config):
 | 
				
			|||||||
    for conf in config.get(CONF_ON_DISCONNECT, []):
 | 
					    for conf in config.get(CONF_ON_DISCONNECT, []):
 | 
				
			||||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
        await automation.build_automation(trigger, [], conf)
 | 
					        await automation.build_automation(trigger, [], conf)
 | 
				
			||||||
 | 
					    for conf in config.get(CONF_ON_PASSKEY_REQUEST, []):
 | 
				
			||||||
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
 | 
					        await automation.build_automation(trigger, [], conf)
 | 
				
			||||||
 | 
					    for conf in config.get(CONF_ON_PASSKEY_NOTIFICATION, []):
 | 
				
			||||||
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
 | 
					        await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf)
 | 
				
			||||||
 | 
					    for conf in config.get(CONF_ON_NUMERIC_COMPARISON_REQUEST, []):
 | 
				
			||||||
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
 | 
					        await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,44 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BLEClientPasskeyRequestTrigger : public Trigger<>, public BLEClientNode {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit BLEClientPasskeyRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
				
			||||||
 | 
					  void loop() override {}
 | 
				
			||||||
 | 
					  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
				
			||||||
 | 
					    if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT &&
 | 
				
			||||||
 | 
					        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
				
			||||||
 | 
					      this->trigger();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BLEClientPasskeyNotificationTrigger : public Trigger<uint32_t>, public BLEClientNode {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit BLEClientPasskeyNotificationTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
				
			||||||
 | 
					  void loop() override {}
 | 
				
			||||||
 | 
					  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
				
			||||||
 | 
					    if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT &&
 | 
				
			||||||
 | 
					        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
				
			||||||
 | 
					      uint32_t passkey = param->ble_security.key_notif.passkey;
 | 
				
			||||||
 | 
					      this->trigger(passkey);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BLEClientNumericComparisonRequestTrigger : public Trigger<uint32_t>, public BLEClientNode {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit BLEClientNumericComparisonRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
				
			||||||
 | 
					  void loop() override {}
 | 
				
			||||||
 | 
					  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
				
			||||||
 | 
					    if (event == ESP_GAP_BLE_NC_REQ_EVT &&
 | 
				
			||||||
 | 
					        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
				
			||||||
 | 
					      uint32_t passkey = param->ble_security.key_notif.passkey;
 | 
				
			||||||
 | 
					      this->trigger(passkey);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BLEWriterClientNode : public BLEClientNode {
 | 
					class BLEWriterClientNode : public BLEClientNode {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  BLEWriterClientNode(BLEClient *ble_client) {
 | 
					  BLEWriterClientNode(BLEClient *ble_client) {
 | 
				
			||||||
@@ -94,6 +132,86 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
 | 
				
			|||||||
  std::function<std::vector<uint8_t>(Ts...)> value_template_{};
 | 
					  std::function<std::vector<uint8_t>(Ts...)> value_template_{};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  BLEClientPasskeyReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    uint32_t passkey;
 | 
				
			||||||
 | 
					    if (has_simple_value_) {
 | 
				
			||||||
 | 
					      passkey = this->value_simple_;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      passkey = this->value_template_(x...);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (passkey > 999999)
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    esp_bd_addr_t remote_bda;
 | 
				
			||||||
 | 
					    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
				
			||||||
 | 
					    esp_ble_passkey_reply(remote_bda, true, passkey);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_value_template(std::function<uint32_t(Ts...)> func) {
 | 
				
			||||||
 | 
					    this->value_template_ = std::move(func);
 | 
				
			||||||
 | 
					    has_simple_value_ = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_value_simple(const uint32_t &value) {
 | 
				
			||||||
 | 
					    this->value_simple_ = value;
 | 
				
			||||||
 | 
					    has_simple_value_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  BLEClient *parent_{nullptr};
 | 
				
			||||||
 | 
					  bool has_simple_value_ = true;
 | 
				
			||||||
 | 
					  uint32_t value_simple_{0};
 | 
				
			||||||
 | 
					  std::function<uint32_t(Ts...)> value_template_{};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    esp_bd_addr_t remote_bda;
 | 
				
			||||||
 | 
					    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
				
			||||||
 | 
					    if (has_simple_value_) {
 | 
				
			||||||
 | 
					      esp_ble_confirm_reply(remote_bda, this->value_simple_);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      esp_ble_confirm_reply(remote_bda, this->value_template_(x...));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_value_template(std::function<bool(Ts...)> func) {
 | 
				
			||||||
 | 
					    this->value_template_ = std::move(func);
 | 
				
			||||||
 | 
					    has_simple_value_ = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_value_simple(const bool &value) {
 | 
				
			||||||
 | 
					    this->value_simple_ = value;
 | 
				
			||||||
 | 
					    has_simple_value_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  BLEClient *parent_{nullptr};
 | 
				
			||||||
 | 
					  bool has_simple_value_ = true;
 | 
				
			||||||
 | 
					  bool value_simple_{false};
 | 
				
			||||||
 | 
					  std::function<bool(Ts...)> value_template_{};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    esp_bd_addr_t remote_bda;
 | 
				
			||||||
 | 
					    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
				
			||||||
 | 
					    esp_ble_remove_bond_device(remote_bda);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  BLEClient *parent_{nullptr};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace ble_client
 | 
					}  // namespace ble_client
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ class BLEClient;
 | 
				
			|||||||
class BLEClientNode {
 | 
					class BLEClientNode {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
					  virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
				
			||||||
                                   esp_ble_gattc_cb_param_t *param) = 0;
 | 
					                                   esp_ble_gattc_cb_param_t *param){};
 | 
				
			||||||
  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {}
 | 
					  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {}
 | 
				
			||||||
  virtual void loop() {}
 | 
					  virtual void loop() {}
 | 
				
			||||||
  void set_address(uint64_t address) { address_ = address; }
 | 
					  void set_address(uint64_t address) { address_ = address; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@ DEVICE = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
NextAction = dfplayer_ns.class_("NextAction", automation.Action)
 | 
					NextAction = dfplayer_ns.class_("NextAction", automation.Action)
 | 
				
			||||||
PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action)
 | 
					PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action)
 | 
				
			||||||
 | 
					PlayMp3Action = dfplayer_ns.class_("PlayMp3Action", automation.Action)
 | 
				
			||||||
PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action)
 | 
					PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action)
 | 
				
			||||||
PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action)
 | 
					PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action)
 | 
				
			||||||
SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action)
 | 
					SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action)
 | 
				
			||||||
@@ -113,6 +114,25 @@ async def dfplayer_previous_to_code(config, action_id, template_arg, args):
 | 
				
			|||||||
    return var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "dfplayer.play_mp3",
 | 
				
			||||||
 | 
					    PlayMp3Action,
 | 
				
			||||||
 | 
					    cv.maybe_simple_value(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.use_id(DFPlayer),
 | 
				
			||||||
 | 
					            cv.Required(CONF_FILE): cv.templatable(cv.int_),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        key=CONF_FILE,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def dfplayer_play_mp3_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg)
 | 
				
			||||||
 | 
					    await cg.register_parented(var, config[CONF_ID])
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_FILE], args, float)
 | 
				
			||||||
 | 
					    cg.add(var.set_file(template_))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@automation.register_action(
 | 
					@automation.register_action(
 | 
				
			||||||
    "dfplayer.play",
 | 
					    "dfplayer.play",
 | 
				
			||||||
    PlayFileAction,
 | 
					    PlayFileAction,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,10 @@ namespace dfplayer {
 | 
				
			|||||||
static const char *const TAG = "dfplayer";
 | 
					static const char *const TAG = "dfplayer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
 | 
					void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
 | 
				
			||||||
  if (folder < 100 && file < 256) {
 | 
					  if (folder <= 10 && file <= 1000) {
 | 
				
			||||||
    this->ack_set_is_playing_ = true;
 | 
					    this->ack_set_is_playing_ = true;
 | 
				
			||||||
    this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
 | 
					    this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
 | 
				
			||||||
  } else if (folder <= 10 && file <= 1000) {
 | 
					  } else if (folder < 100 && file < 256) {
 | 
				
			||||||
    this->ack_set_is_playing_ = true;
 | 
					    this->ack_set_is_playing_ = true;
 | 
				
			||||||
    this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file);
 | 
					    this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,10 @@ class DFPlayer : public uart::UARTDevice, public Component {
 | 
				
			|||||||
    this->ack_set_is_playing_ = true;
 | 
					    this->ack_set_is_playing_ = true;
 | 
				
			||||||
    this->send_cmd_(0x02);
 | 
					    this->send_cmd_(0x02);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  void play_mp3(uint16_t file) {
 | 
				
			||||||
 | 
					    this->ack_set_is_playing_ = true;
 | 
				
			||||||
 | 
					    this->send_cmd_(0x12, file);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  void play_file(uint16_t file) {
 | 
					  void play_file(uint16_t file) {
 | 
				
			||||||
    this->ack_set_is_playing_ = true;
 | 
					    this->ack_set_is_playing_ = true;
 | 
				
			||||||
    this->send_cmd_(0x03, file);
 | 
					    this->send_cmd_(0x03, file);
 | 
				
			||||||
@@ -113,6 +117,16 @@ class DFPlayer : public uart::UARTDevice, public Component {
 | 
				
			|||||||
DFPLAYER_SIMPLE_ACTION(NextAction, next)
 | 
					DFPLAYER_SIMPLE_ACTION(NextAction, next)
 | 
				
			||||||
DFPLAYER_SIMPLE_ACTION(PreviousAction, previous)
 | 
					DFPLAYER_SIMPLE_ACTION(PreviousAction, previous)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class PlayMp3Action : public Action<Ts...>, public Parented<DFPlayer> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint16_t, file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    auto file = this->file_.value(x...);
 | 
				
			||||||
 | 
					    this->parent_->play_mp3(file);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Parented<DFPlayer> {
 | 
					template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Parented<DFPlayer> {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  TEMPLATABLE_VALUE(uint16_t, file)
 | 
					  TEMPLATABLE_VALUE(uint16_t, file)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ CODEOWNERS = ["@jesserockz"]
 | 
				
			|||||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
					CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_BLE_ID = "ble_id"
 | 
					CONF_BLE_ID = "ble_id"
 | 
				
			||||||
 | 
					CONF_IO_CAPABILITY = "io_capability"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
					NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,10 +20,21 @@ GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
 | 
				
			|||||||
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
 | 
					GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
 | 
				
			||||||
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
 | 
					GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IoCapability = esp32_ble_ns.enum("IoCapability")
 | 
				
			||||||
 | 
					IO_CAPABILITY = {
 | 
				
			||||||
 | 
					    "none": IoCapability.IO_CAP_NONE,
 | 
				
			||||||
 | 
					    "keyboard_only": IoCapability.IO_CAP_IN,
 | 
				
			||||||
 | 
					    "keyboard_display": IoCapability.IO_CAP_KBDISP,
 | 
				
			||||||
 | 
					    "display_only": IoCapability.IO_CAP_OUT,
 | 
				
			||||||
 | 
					    "display_yes_no": IoCapability.IO_CAP_IO,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema(
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(ESP32BLE),
 | 
					        cv.GenerateID(): cv.declare_id(ESP32BLE),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
 | 
				
			||||||
 | 
					            IO_CAPABILITY, lower=True
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
).extend(cv.COMPONENT_SCHEMA)
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,6 +51,7 @@ FINAL_VALIDATE_SCHEMA = validate_variant
 | 
				
			|||||||
async def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    await cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CORE.using_esp_idf:
 | 
					    if CORE.using_esp_idf:
 | 
				
			||||||
        add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
 | 
					        add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,8 +134,7 @@ bool ESP32BLE::ble_setup_() {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
 | 
					  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(uint8_t));
 | 
				
			||||||
  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
 | 
					 | 
				
			||||||
  if (err != ESP_OK) {
 | 
					  if (err != ESP_OK) {
 | 
				
			||||||
    ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err);
 | 
					    ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err);
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
@@ -215,9 +214,31 @@ float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
 | 
				
			|||||||
void ESP32BLE::dump_config() {
 | 
					void ESP32BLE::dump_config() {
 | 
				
			||||||
  const uint8_t *mac_address = esp_bt_dev_get_address();
 | 
					  const uint8_t *mac_address = esp_bt_dev_get_address();
 | 
				
			||||||
  if (mac_address) {
 | 
					  if (mac_address) {
 | 
				
			||||||
 | 
					    const char *io_capability_s;
 | 
				
			||||||
 | 
					    switch (this->io_cap_) {
 | 
				
			||||||
 | 
					      case ESP_IO_CAP_OUT:
 | 
				
			||||||
 | 
					        io_capability_s = "display_only";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ESP_IO_CAP_IO:
 | 
				
			||||||
 | 
					        io_capability_s = "display_yes_no";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ESP_IO_CAP_IN:
 | 
				
			||||||
 | 
					        io_capability_s = "keyboard_only";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ESP_IO_CAP_NONE:
 | 
				
			||||||
 | 
					        io_capability_s = "none";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case ESP_IO_CAP_KBDISP:
 | 
				
			||||||
 | 
					        io_capability_s = "keyboard_display";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        io_capability_s = "invalid";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE:");
 | 
					    ESP_LOGCONFIG(TAG, "ESP32 BLE:");
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
 | 
					    ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
 | 
				
			||||||
                  mac_address[3], mac_address[4], mac_address[5]);
 | 
					                  mac_address[3], mac_address[4], mac_address[5]);
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  IO Capability: %s", io_capability_s);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
 | 
					    ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,14 @@ typedef struct {
 | 
				
			|||||||
  uint16_t mtu;
 | 
					  uint16_t mtu;
 | 
				
			||||||
} conn_status_t;
 | 
					} conn_status_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum IoCapability {
 | 
				
			||||||
 | 
					  IO_CAP_OUT = ESP_IO_CAP_OUT,
 | 
				
			||||||
 | 
					  IO_CAP_IO = ESP_IO_CAP_IO,
 | 
				
			||||||
 | 
					  IO_CAP_IN = ESP_IO_CAP_IN,
 | 
				
			||||||
 | 
					  IO_CAP_NONE = ESP_IO_CAP_NONE,
 | 
				
			||||||
 | 
					  IO_CAP_KBDISP = ESP_IO_CAP_KBDISP,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GAPEventHandler {
 | 
					class GAPEventHandler {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
 | 
					  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
 | 
				
			||||||
@@ -44,6 +52,8 @@ class GATTsEventHandler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ESP32BLE : public Component {
 | 
					class ESP32BLE : public Component {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setup() override;
 | 
					  void setup() override;
 | 
				
			||||||
  void loop() override;
 | 
					  void loop() override;
 | 
				
			||||||
  void dump_config() override;
 | 
					  void dump_config() override;
 | 
				
			||||||
@@ -72,6 +82,7 @@ class ESP32BLE : public Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Queue<BLEEvent> ble_events_;
 | 
					  Queue<BLEEvent> ble_events_;
 | 
				
			||||||
  BLEAdvertising *advertising_;
 | 
					  BLEAdvertising *advertising_;
 | 
				
			||||||
 | 
					  esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
					// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,10 +77,12 @@ void FingerprintGrowComponent::finish_enrollment(uint8_t result) {
 | 
				
			|||||||
    this->enrollment_done_callback_.call(this->enrollment_slot_);
 | 
					    this->enrollment_done_callback_.call(this->enrollment_slot_);
 | 
				
			||||||
    this->get_fingerprint_count_();
 | 
					    this->get_fingerprint_count_();
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
 | 
				
			||||||
      this->enrollment_failed_callback_.call(this->enrollment_slot_);
 | 
					      this->enrollment_failed_callback_.call(this->enrollment_slot_);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  this->enrollment_image_ = 0;
 | 
					  this->enrollment_image_ = 0;
 | 
				
			||||||
  this->enrollment_slot_ = 0;
 | 
					  this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
 | 
				
			||||||
  if (this->enrolling_binary_sensor_ != nullptr) {
 | 
					  if (this->enrolling_binary_sensor_ != nullptr) {
 | 
				
			||||||
    this->enrolling_binary_sensor_->publish_state(false);
 | 
					    this->enrolling_binary_sensor_->publish_state(false);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ namespace fingerprint_grow {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const uint16_t START_CODE = 0xEF01;
 | 
					static const uint16_t START_CODE = 0xEF01;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint16_t ENROLLMENT_SLOT_UNUSED = 0xFFFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum GrowPacketType {
 | 
					enum GrowPacketType {
 | 
				
			||||||
  COMMAND = 0x01,
 | 
					  COMMAND = 0x01,
 | 
				
			||||||
  DATA = 0x02,
 | 
					  DATA = 0x02,
 | 
				
			||||||
@@ -158,7 +160,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
 | 
				
			|||||||
  uint32_t new_password_ = -1;
 | 
					  uint32_t new_password_ = -1;
 | 
				
			||||||
  GPIOPin *sensing_pin_{nullptr};
 | 
					  GPIOPin *sensing_pin_{nullptr};
 | 
				
			||||||
  uint8_t enrollment_image_ = 0;
 | 
					  uint8_t enrollment_image_ = 0;
 | 
				
			||||||
  uint16_t enrollment_slot_ = 0;
 | 
					  uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
 | 
				
			||||||
  uint8_t enrollment_buffers_ = 5;
 | 
					  uint8_t enrollment_buffers_ = 5;
 | 
				
			||||||
  bool waiting_removal_ = false;
 | 
					  bool waiting_removal_ = false;
 | 
				
			||||||
  uint32_t last_aura_led_control_ = 0;
 | 
					  uint32_t last_aura_led_control_ = 0;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								esphome/components/hyt271/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/hyt271/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					CODEOWNERS = ["@Philippe12"]
 | 
				
			||||||
							
								
								
									
										52
									
								
								esphome/components/hyt271/hyt271.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphome/components/hyt271/hyt271.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					#include "hyt271.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					#include "esphome/core/hal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace hyt271 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "hyt271";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint8_t HYT271_ADDRESS = 0x28;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HYT271Component::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "HYT271:");
 | 
				
			||||||
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
 | 
					  LOG_UPDATE_INTERVAL(this);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature", this->temperature_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Humidity", this->humidity_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void HYT271Component::update() {
 | 
				
			||||||
 | 
					  uint8_t raw_data[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->write(&raw_data[0], 0) != i2c::ERROR_OK) {
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication with HYT271 failed! => Ask new values");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->set_timeout("wait_convert", 50, [this]() {
 | 
				
			||||||
 | 
					    uint8_t raw_data[4];
 | 
				
			||||||
 | 
					    if (this->read(raw_data, 4) != i2c::ERROR_OK) {
 | 
				
			||||||
 | 
					      this->status_set_warning();
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "Communication with HYT271 failed! => Read values");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    uint16_t raw_temperature = ((raw_data[2] << 8) | raw_data[3]) >> 2;
 | 
				
			||||||
 | 
					    uint16_t raw_humidity = ((raw_data[0] & 0x3F) << 8) | raw_data[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float temperature = ((float(raw_temperature)) * (165.0f / 16383.0f)) - 40.0f;
 | 
				
			||||||
 | 
					    float humidity = (float(raw_humidity)) * (100.0f / 16383.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this->temperature_ != nullptr)
 | 
				
			||||||
 | 
					      this->temperature_->publish_state(temperature);
 | 
				
			||||||
 | 
					    if (this->humidity_ != nullptr)
 | 
				
			||||||
 | 
					      this->humidity_->publish_state(humidity);
 | 
				
			||||||
 | 
					    this->status_clear_warning();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					float HYT271Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace hyt271
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										27
									
								
								esphome/components/hyt271/hyt271.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/hyt271/hyt271.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace hyt271 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HYT271Component : public PollingComponent, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
 | 
				
			||||||
 | 
					  void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  /// Update the sensor values (temperature+humidity).
 | 
				
			||||||
 | 
					  void update() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *humidity_{nullptr};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace hyt271
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										56
									
								
								esphome/components/hyt271/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								esphome/components/hyt271/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import i2c, sensor
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hyt271_ns = cg.esphome_ns.namespace("hyt271")
 | 
				
			||||||
 | 
					HYT271Component = hyt271_ns.class_(
 | 
				
			||||||
 | 
					    "HYT271Component", cg.PollingComponent, i2c.I2CDevice
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(HYT271Component),
 | 
				
			||||||
 | 
					            cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                accuracy_decimals=1,
 | 
				
			||||||
 | 
					                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Required(CONF_HUMIDITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                accuracy_decimals=1,
 | 
				
			||||||
 | 
					                device_class=DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					                state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x28))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_TEMPERATURE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
				
			||||||
 | 
					        cg.add(var.set_temperature(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_HUMIDITY in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
				
			||||||
 | 
					        cg.add(var.set_humidity(sens))
 | 
				
			||||||
							
								
								
									
										148
									
								
								esphome/components/max6956/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								esphome/components/max6956/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome import pins, automation
 | 
				
			||||||
 | 
					from esphome.components import i2c
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_NUMBER,
 | 
				
			||||||
 | 
					    CONF_MODE,
 | 
				
			||||||
 | 
					    CONF_INVERTED,
 | 
				
			||||||
 | 
					    CONF_INPUT,
 | 
				
			||||||
 | 
					    CONF_OUTPUT,
 | 
				
			||||||
 | 
					    CONF_PULLUP,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@looping40"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_BRIGHTNESS_MODE = "brightness_mode"
 | 
				
			||||||
 | 
					CONF_BRIGHTNESS_GLOBAL = "brightness_global"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					max6956_ns = cg.esphome_ns.namespace("max6956")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX6956 = max6956_ns.class_("MAX6956", cg.Component, i2c.I2CDevice)
 | 
				
			||||||
 | 
					MAX6956GPIOPin = max6956_ns.class_("MAX6956GPIOPin", cg.GPIOPin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Actions
 | 
				
			||||||
 | 
					SetCurrentGlobalAction = max6956_ns.class_("SetCurrentGlobalAction", automation.Action)
 | 
				
			||||||
 | 
					SetCurrentModeAction = max6956_ns.class_("SetCurrentModeAction", automation.Action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX6956_CURRENTMODE = max6956_ns.enum("MAX6956CURRENTMODE")
 | 
				
			||||||
 | 
					CURRENT_MODES = {
 | 
				
			||||||
 | 
					    "global": MAX6956_CURRENTMODE.GLOBAL,
 | 
				
			||||||
 | 
					    "segment": MAX6956_CURRENTMODE.SEGMENT,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.Required(CONF_ID): cv.declare_id(MAX6956),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_BRIGHTNESS_GLOBAL, default="0"): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=15
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_BRIGHTNESS_MODE, default="global"): cv.enum(
 | 
				
			||||||
 | 
					                CURRENT_MODES, lower=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x40))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					    cg.add(var.set_brightness_mode(config[CONF_BRIGHTNESS_MODE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_brightness_global(config[CONF_BRIGHTNESS_GLOBAL]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_mode(value):
 | 
				
			||||||
 | 
					    if not (value[CONF_INPUT] or value[CONF_OUTPUT]):
 | 
				
			||||||
 | 
					        raise cv.Invalid("Mode must be either input or output")
 | 
				
			||||||
 | 
					    if value[CONF_INPUT] and value[CONF_OUTPUT]:
 | 
				
			||||||
 | 
					        raise cv.Invalid("Mode must be either input or output")
 | 
				
			||||||
 | 
					    if value[CONF_PULLUP] and not value[CONF_INPUT]:
 | 
				
			||||||
 | 
					        raise cv.Invalid("Pullup only available with input")
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_MAX6956 = "max6956"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX6956_PIN_SCHEMA = cv.All(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(): cv.declare_id(MAX6956GPIOPin),
 | 
				
			||||||
 | 
					        cv.Required(CONF_MAX6956): cv.use_id(MAX6956),
 | 
				
			||||||
 | 
					        cv.Required(CONF_NUMBER): cv.int_range(min=4, max=31),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_MODE, default={}): cv.All(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.Optional(CONF_INPUT, default=False): cv.boolean,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PULLUP, default=False): cv.boolean,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            validate_mode,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_INVERTED, default=False): cv.boolean,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pins.PIN_SCHEMA_REGISTRY.register(CONF_MAX6956, MAX6956_PIN_SCHEMA)
 | 
				
			||||||
 | 
					async def max6956_pin_to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_MAX6956])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cg.add(var.set_parent(parent))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    num = config[CONF_NUMBER]
 | 
				
			||||||
 | 
					    cg.add(var.set_pin(num))
 | 
				
			||||||
 | 
					    cg.add(var.set_inverted(config[CONF_INVERTED]))
 | 
				
			||||||
 | 
					    cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "max6956.set_brightness_global",
 | 
				
			||||||
 | 
					    SetCurrentGlobalAction,
 | 
				
			||||||
 | 
					    cv.maybe_simple_value(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(CONF_ID): cv.use_id(MAX6956),
 | 
				
			||||||
 | 
					            cv.Required(CONF_BRIGHTNESS_GLOBAL): cv.templatable(
 | 
				
			||||||
 | 
					                cv.int_range(min=0, max=15)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        key=CONF_BRIGHTNESS_GLOBAL,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def max6956_set_brightness_global_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    paren = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_BRIGHTNESS_GLOBAL], args, float)
 | 
				
			||||||
 | 
					    cg.add(var.set_brightness_global(template_))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "max6956.set_brightness_mode",
 | 
				
			||||||
 | 
					    SetCurrentModeAction,
 | 
				
			||||||
 | 
					    cv.maybe_simple_value(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.Required(CONF_ID): cv.use_id(MAX6956),
 | 
				
			||||||
 | 
					            cv.Required(CONF_BRIGHTNESS_MODE): cv.templatable(
 | 
				
			||||||
 | 
					                cv.enum(CURRENT_MODES, lower=True)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        key=CONF_BRIGHTNESS_MODE,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def max6956_set_brightness_mode_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    paren = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_BRIGHTNESS_MODE], args, float)
 | 
				
			||||||
 | 
					    cg.add(var.set_brightness_mode(template_))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
							
								
								
									
										40
									
								
								esphome/components/max6956/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/max6956/automation.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/automation.h"
 | 
				
			||||||
 | 
					#include "esphome/components/max6956/max6956.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace max6956 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class SetCurrentGlobalAction : public Action<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  SetCurrentGlobalAction(MAX6956 *max6956) : max6956_(max6956) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, brightness_global)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    this->max6956_->set_brightness_global(this->brightness_global_.value(x...));
 | 
				
			||||||
 | 
					    this->max6956_->write_brightness_global();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  MAX6956 *max6956_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class SetCurrentModeAction : public Action<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  SetCurrentModeAction(MAX6956 *max6956) : max6956_(max6956) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(max6956::MAX6956CURRENTMODE, brightness_mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    this->max6956_->set_brightness_mode(this->brightness_mode_.value(x...));
 | 
				
			||||||
 | 
					    this->max6956_->write_brightness_mode();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  MAX6956 *max6956_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					}  // namespace max6956
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										170
									
								
								esphome/components/max6956/max6956.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								esphome/components/max6956/max6956.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					#include "max6956.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace max6956 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "max6956";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Masks for MAX6956 Configuration register
 | 
				
			||||||
 | 
					const uint32_t MASK_TRANSITION_DETECTION = 0x80;
 | 
				
			||||||
 | 
					const uint32_t MASK_INDIVIDUAL_CURRENT = 0x40;
 | 
				
			||||||
 | 
					const uint32_t MASK_NORMAL_OPERATION = 0x01;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint32_t MASK_1PORT_VALUE = 0x03;
 | 
				
			||||||
 | 
					const uint32_t MASK_PORT_CONFIG = 0x03;
 | 
				
			||||||
 | 
					const uint8_t MASK_CONFIG_CURRENT = 0x40;
 | 
				
			||||||
 | 
					const uint8_t MASK_CURRENT_PIN = 0x0F;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**************************************
 | 
				
			||||||
 | 
					 *    MAX6956                         *
 | 
				
			||||||
 | 
					 **************************************/
 | 
				
			||||||
 | 
					void MAX6956::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up MAX6956...");
 | 
				
			||||||
 | 
					  uint8_t configuration;
 | 
				
			||||||
 | 
					  if (!this->read_reg_(MAX6956_CONFIGURATION, &configuration)) {
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  write_brightness_global();
 | 
				
			||||||
 | 
					  write_brightness_mode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** TO DO : read transition detection in yaml
 | 
				
			||||||
 | 
					      TO DO : read indivdual current in yaml **/
 | 
				
			||||||
 | 
					  this->read_reg_(MAX6956_CONFIGURATION, &configuration);
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Initial reg[0x%.2X]=0x%.2X", MAX6956_CONFIGURATION, configuration);
 | 
				
			||||||
 | 
					  configuration = configuration | MASK_NORMAL_OPERATION;
 | 
				
			||||||
 | 
					  this->write_reg_(MAX6956_CONFIGURATION, configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Enabling normal operation");
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "setup reg[0x%.2X]=0x%.2X", MAX6956_CONFIGURATION, configuration);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MAX6956::digital_read(uint8_t pin) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_1PORT_VALUE_START + pin;
 | 
				
			||||||
 | 
					  uint8_t value = 0;
 | 
				
			||||||
 | 
					  this->read_reg_(reg_addr, &value);
 | 
				
			||||||
 | 
					  return (value & MASK_1PORT_VALUE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::digital_write(uint8_t pin, bool value) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_1PORT_VALUE_START + pin;
 | 
				
			||||||
 | 
					  this->write_reg_(reg_addr, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::pin_mode(uint8_t pin, gpio::Flags flags) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_PORT_CONFIG_START + (pin - MAX6956_MIN) / 4;
 | 
				
			||||||
 | 
					  uint8_t config = 0;
 | 
				
			||||||
 | 
					  uint8_t shift = 2 * (pin % 4);
 | 
				
			||||||
 | 
					  MAX6956GPIOMode mode = MAX6956_INPUT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (flags == gpio::FLAG_INPUT) {
 | 
				
			||||||
 | 
					    mode = MAX6956GPIOMode::MAX6956_INPUT;
 | 
				
			||||||
 | 
					  } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
 | 
				
			||||||
 | 
					    mode = MAX6956GPIOMode::MAX6956_INPUT_PULLUP;
 | 
				
			||||||
 | 
					  } else if (flags == gpio::FLAG_OUTPUT) {
 | 
				
			||||||
 | 
					    mode = MAX6956GPIOMode::MAX6956_OUTPUT;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->read_reg_(reg_addr, &config);
 | 
				
			||||||
 | 
					  config &= ~(MASK_PORT_CONFIG << shift);
 | 
				
			||||||
 | 
					  config |= (mode << shift);
 | 
				
			||||||
 | 
					  this->write_reg_(reg_addr, config);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::pin_mode(uint8_t pin, max6956::MAX6956GPIOFlag flags) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_PORT_CONFIG_START + (pin - MAX6956_MIN) / 4;
 | 
				
			||||||
 | 
					  uint8_t config = 0;
 | 
				
			||||||
 | 
					  uint8_t shift = 2 * (pin % 4);
 | 
				
			||||||
 | 
					  MAX6956GPIOMode mode = MAX6956GPIOMode::MAX6956_LED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (flags == max6956::FLAG_LED) {
 | 
				
			||||||
 | 
					    mode = MAX6956GPIOMode::MAX6956_LED;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->read_reg_(reg_addr, &config);
 | 
				
			||||||
 | 
					  config &= ~(MASK_PORT_CONFIG << shift);
 | 
				
			||||||
 | 
					  config |= (mode << shift);
 | 
				
			||||||
 | 
					  this->write_reg_(reg_addr, config);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::set_brightness_global(uint8_t current) {
 | 
				
			||||||
 | 
					  if (current > 15) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Global brightness out off range (%u)", current);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  global_brightness_ = current;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::write_brightness_global() { this->write_reg_(MAX6956_GLOBAL_CURRENT, global_brightness_); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::set_brightness_mode(max6956::MAX6956CURRENTMODE brightness_mode) { brightness_mode_ = brightness_mode; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::write_brightness_mode() {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_CONFIGURATION;
 | 
				
			||||||
 | 
					  uint8_t config = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->read_reg_(reg_addr, &config);
 | 
				
			||||||
 | 
					  config &= ~MASK_CONFIG_CURRENT;
 | 
				
			||||||
 | 
					  config |= brightness_mode_ << 6;
 | 
				
			||||||
 | 
					  this->write_reg_(reg_addr, config);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::set_pin_brightness(uint8_t pin, float brightness) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = MAX6956_CURRENT_START + (pin - MAX6956_MIN) / 2;
 | 
				
			||||||
 | 
					  uint8_t config = 0;
 | 
				
			||||||
 | 
					  uint8_t shift = 4 * (pin % 2);
 | 
				
			||||||
 | 
					  uint8_t bright = roundf(brightness * 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (prev_bright_[pin - MAX6956_MIN] == bright)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  prev_bright_[pin - MAX6956_MIN] = bright;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->read_reg_(reg_addr, &config);
 | 
				
			||||||
 | 
					  config &= ~(MASK_CURRENT_PIN << shift);
 | 
				
			||||||
 | 
					  config |= (bright << shift);
 | 
				
			||||||
 | 
					  this->write_reg_(reg_addr, config);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MAX6956::read_reg_(uint8_t reg, uint8_t *value) {
 | 
				
			||||||
 | 
					  if (this->is_failed())
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return this->read_byte(reg, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool MAX6956::write_reg_(uint8_t reg, uint8_t value) {
 | 
				
			||||||
 | 
					  if (this->is_failed())
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return this->write_byte(reg, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "MAX6956");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (brightness_mode_ == MAX6956CURRENTMODE::GLOBAL) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "current mode: global");
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "global brightness: %u", global_brightness_);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "current mode: segment");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**************************************
 | 
				
			||||||
 | 
					 *    MAX6956GPIOPin                  *
 | 
				
			||||||
 | 
					 **************************************/
 | 
				
			||||||
 | 
					void MAX6956GPIOPin::setup() { pin_mode(flags_); }
 | 
				
			||||||
 | 
					void MAX6956GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
 | 
				
			||||||
 | 
					bool MAX6956GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
 | 
				
			||||||
 | 
					void MAX6956GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
 | 
				
			||||||
 | 
					std::string MAX6956GPIOPin::dump_summary() const {
 | 
				
			||||||
 | 
					  char buffer[32];
 | 
				
			||||||
 | 
					  snprintf(buffer, sizeof(buffer), "%u via Max6956", pin_);
 | 
				
			||||||
 | 
					  return buffer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace max6956
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										94
									
								
								esphome/components/max6956/max6956.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								esphome/components/max6956/max6956.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/hal.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace max6956 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Modes for MAX6956 pins
 | 
				
			||||||
 | 
					enum MAX6956GPIOMode : uint8_t {
 | 
				
			||||||
 | 
					  MAX6956_LED = 0x00,
 | 
				
			||||||
 | 
					  MAX6956_OUTPUT = 0x01,
 | 
				
			||||||
 | 
					  MAX6956_INPUT = 0x02,
 | 
				
			||||||
 | 
					  MAX6956_INPUT_PULLUP = 0x03
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Range for MAX6956 pins
 | 
				
			||||||
 | 
					enum MAX6956GPIORange : uint8_t {
 | 
				
			||||||
 | 
					  MAX6956_MIN = 4,
 | 
				
			||||||
 | 
					  MAX6956_MAX = 31,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MAX6956GPIORegisters {
 | 
				
			||||||
 | 
					  MAX6956_GLOBAL_CURRENT = 0x02,
 | 
				
			||||||
 | 
					  MAX6956_CONFIGURATION = 0x04,
 | 
				
			||||||
 | 
					  MAX6956_TRANSITION_DETECT_MASK = 0x06,
 | 
				
			||||||
 | 
					  MAX6956_DISPLAY_TEST = 0x07,
 | 
				
			||||||
 | 
					  MAX6956_PORT_CONFIG_START = 0x09,   // Port Configuration P7, P6, P5, P4
 | 
				
			||||||
 | 
					  MAX6956_CURRENT_START = 0x12,       // Current054
 | 
				
			||||||
 | 
					  MAX6956_1PORT_VALUE_START = 0x20,   // Port 0 only (virtual port, no action)
 | 
				
			||||||
 | 
					  MAX6956_8PORTS_VALUE_START = 0x44,  // 8 ports 4–11 (data bits D0–D7)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MAX6956GPIOFlag { FLAG_LED = 0x20 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MAX6956CURRENTMODE { GLOBAL = 0x00, SEGMENT = 0x01 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MAX6956 : public Component, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  MAX6956() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool digital_read(uint8_t pin);
 | 
				
			||||||
 | 
					  void digital_write(uint8_t pin, bool value);
 | 
				
			||||||
 | 
					  void pin_mode(uint8_t pin, gpio::Flags flags);
 | 
				
			||||||
 | 
					  void pin_mode(uint8_t pin, max6956::MAX6956GPIOFlag flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float get_setup_priority() const override { return setup_priority::HARDWARE; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_brightness_global(uint8_t current);
 | 
				
			||||||
 | 
					  void set_brightness_mode(max6956::MAX6956CURRENTMODE brightness_mode);
 | 
				
			||||||
 | 
					  void set_pin_brightness(uint8_t pin, float brightness);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void write_brightness_global();
 | 
				
			||||||
 | 
					  void write_brightness_mode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  // read a given register
 | 
				
			||||||
 | 
					  bool read_reg_(uint8_t reg, uint8_t *value);
 | 
				
			||||||
 | 
					  // write a value to a given register
 | 
				
			||||||
 | 
					  bool write_reg_(uint8_t reg, uint8_t value);
 | 
				
			||||||
 | 
					  max6956::MAX6956CURRENTMODE brightness_mode_;
 | 
				
			||||||
 | 
					  uint8_t global_brightness_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  int8_t prev_bright_[28] = {0};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MAX6956GPIOPin : public GPIOPin {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void pin_mode(gpio::Flags flags) override;
 | 
				
			||||||
 | 
					  bool digital_read() override;
 | 
				
			||||||
 | 
					  void digital_write(bool value) override;
 | 
				
			||||||
 | 
					  std::string dump_summary() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_parent(MAX6956 *parent) { parent_ = parent; }
 | 
				
			||||||
 | 
					  void set_pin(uint8_t pin) { pin_ = pin; }
 | 
				
			||||||
 | 
					  void set_inverted(bool inverted) { inverted_ = inverted; }
 | 
				
			||||||
 | 
					  void set_flags(gpio::Flags flags) { flags_ = flags; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  MAX6956 *parent_;
 | 
				
			||||||
 | 
					  uint8_t pin_;
 | 
				
			||||||
 | 
					  bool inverted_;
 | 
				
			||||||
 | 
					  gpio::Flags flags_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace max6956
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										28
									
								
								esphome/components/max6956/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/max6956/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import output
 | 
				
			||||||
 | 
					from esphome.const import CONF_PIN, CONF_ID
 | 
				
			||||||
 | 
					from .. import MAX6956, max6956_ns, CONF_MAX6956
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["max6956"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX6956LedChannel = max6956_ns.class_(
 | 
				
			||||||
 | 
					    "MAX6956LedChannel", output.FloatOutput, cg.Component
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ID): cv.declare_id(MAX6956LedChannel),
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_MAX6956): cv.use_id(MAX6956),
 | 
				
			||||||
 | 
					        cv.Required(CONF_PIN): cv.int_range(min=4, max=31),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_MAX6956])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await output.register_output(var, config)
 | 
				
			||||||
 | 
					    cg.add(var.set_pin(config[CONF_PIN]))
 | 
				
			||||||
 | 
					    cg.add(var.set_parent(parent))
 | 
				
			||||||
							
								
								
									
										26
									
								
								esphome/components/max6956/output/max6956_led_output.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/max6956/output/max6956_led_output.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					#include "max6956_led_output.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace max6956 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "max6956_led_channel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956LedChannel::write_state(float state) { this->parent_->set_pin_brightness(this->pin_, state); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956LedChannel::write_state(bool state) { this->parent_->digital_write(this->pin_, state); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956LedChannel::setup() {
 | 
				
			||||||
 | 
					  this->parent_->pin_mode(this->pin_, max6956::FLAG_LED);
 | 
				
			||||||
 | 
					  this->turn_off();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MAX6956LedChannel::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "MAX6956 current:");
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  MAX6956 pin: %d", this->pin_);
 | 
				
			||||||
 | 
					  LOG_FLOAT_OUTPUT(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace max6956
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										28
									
								
								esphome/components/max6956/output/max6956_led_output.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/max6956/output/max6956_led_output.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/components/max6956/max6956.h"
 | 
				
			||||||
 | 
					#include "esphome/components/output/float_output.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace max6956 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MAX6956;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MAX6956LedChannel : public output::FloatOutput, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_parent(MAX6956 *parent) { this->parent_ = parent; }
 | 
				
			||||||
 | 
					  void set_pin(uint8_t pin) { pin_ = pin; }
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override { return setup_priority::HARDWARE; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void write_state(float state) override;
 | 
				
			||||||
 | 
					  void write_state(bool state) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  MAX6956 *parent_;
 | 
				
			||||||
 | 
					  uint8_t pin_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace max6956
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										78
									
								
								esphome/components/pca6416a/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								esphome/components/pca6416a/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome import pins
 | 
				
			||||||
 | 
					from esphome.components import i2c
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_INPUT,
 | 
				
			||||||
 | 
					    CONF_NUMBER,
 | 
				
			||||||
 | 
					    CONF_MODE,
 | 
				
			||||||
 | 
					    CONF_INVERTED,
 | 
				
			||||||
 | 
					    CONF_OUTPUT,
 | 
				
			||||||
 | 
					    CONF_PULLUP,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@Mat931"]
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					pca6416a_ns = cg.esphome_ns.namespace("pca6416a")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PCA6416AComponent = pca6416a_ns.class_("PCA6416AComponent", cg.Component, i2c.I2CDevice)
 | 
				
			||||||
 | 
					PCA6416AGPIOPin = pca6416a_ns.class_(
 | 
				
			||||||
 | 
					    "PCA6416AGPIOPin", cg.GPIOPin, cg.Parented.template(PCA6416AComponent)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_PCA6416A = "pca6416a"
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    cv.Schema({cv.Required(CONF_ID): cv.declare_id(PCA6416AComponent)})
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x21))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_mode(value):
 | 
				
			||||||
 | 
					    if not (value[CONF_INPUT] or value[CONF_OUTPUT]):
 | 
				
			||||||
 | 
					        raise cv.Invalid("Mode must be either input or output")
 | 
				
			||||||
 | 
					    if value[CONF_INPUT] and value[CONF_OUTPUT]:
 | 
				
			||||||
 | 
					        raise cv.Invalid("Mode must be either input or output")
 | 
				
			||||||
 | 
					    if value[CONF_PULLUP] and not value[CONF_INPUT]:
 | 
				
			||||||
 | 
					        raise cv.Invalid("Pullup only available with input")
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PCA6416A_PIN_SCHEMA = cv.All(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(): cv.declare_id(PCA6416AGPIOPin),
 | 
				
			||||||
 | 
					        cv.Required(CONF_PCA6416A): cv.use_id(PCA6416AComponent),
 | 
				
			||||||
 | 
					        cv.Required(CONF_NUMBER): cv.int_range(min=0, max=16),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_MODE, default={}): cv.All(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.Optional(CONF_INPUT, default=False): cv.boolean,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PULLUP, default=False): cv.boolean,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            validate_mode,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_INVERTED, default=False): cv.boolean,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pins.PIN_SCHEMA_REGISTRY.register("pca6416a", PCA6416A_PIN_SCHEMA)
 | 
				
			||||||
 | 
					async def pca6416a_pin_to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_PCA6416A])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cg.add(var.set_parent(parent))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    num = config[CONF_NUMBER]
 | 
				
			||||||
 | 
					    cg.add(var.set_pin(num))
 | 
				
			||||||
 | 
					    cg.add(var.set_inverted(config[CONF_INVERTED]))
 | 
				
			||||||
 | 
					    cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
							
								
								
									
										174
									
								
								esphome/components/pca6416a/pca6416a.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								esphome/components/pca6416a/pca6416a.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					#include "pca6416a.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace pca6416a {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum PCA6416AGPIORegisters {
 | 
				
			||||||
 | 
					  // 0 side
 | 
				
			||||||
 | 
					  PCA6416A_INPUT0 = 0x00,
 | 
				
			||||||
 | 
					  PCA6416A_OUTPUT0 = 0x02,
 | 
				
			||||||
 | 
					  PCA6416A_INVERT0 = 0x04,
 | 
				
			||||||
 | 
					  PCA6416A_CONFIG0 = 0x06,
 | 
				
			||||||
 | 
					  PCAL6416A_PULL_EN0 = 0x46,
 | 
				
			||||||
 | 
					  PCAL6416A_PULL_DIR0 = 0x48,
 | 
				
			||||||
 | 
					  // 1 side
 | 
				
			||||||
 | 
					  PCA6416A_INPUT1 = 0x01,
 | 
				
			||||||
 | 
					  PCA6416A_OUTPUT1 = 0x03,
 | 
				
			||||||
 | 
					  PCA6416A_INVERT1 = 0x05,
 | 
				
			||||||
 | 
					  PCA6416A_CONFIG1 = 0x07,
 | 
				
			||||||
 | 
					  PCAL6416A_PULL_EN1 = 0x47,
 | 
				
			||||||
 | 
					  PCAL6416A_PULL_DIR1 = 0x49,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "pca6416a";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AComponent::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up PCA6416A...");
 | 
				
			||||||
 | 
					  // Test to see if device exists
 | 
				
			||||||
 | 
					  uint8_t value;
 | 
				
			||||||
 | 
					  if (!this->read_register_(PCA6416A_INPUT0, &value)) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "PCA6416A not available under 0x%02X", this->address_);
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Test to see if the device supports pull-up resistors
 | 
				
			||||||
 | 
					  if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == esphome::i2c::ERROR_OK) {
 | 
				
			||||||
 | 
					    this->has_pullup_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // No polarity inversion
 | 
				
			||||||
 | 
					  this->write_register_(PCA6416A_INVERT0, 0);
 | 
				
			||||||
 | 
					  this->write_register_(PCA6416A_INVERT1, 0);
 | 
				
			||||||
 | 
					  // Set all pins to input
 | 
				
			||||||
 | 
					  this->write_register_(PCA6416A_CONFIG0, 0xff);
 | 
				
			||||||
 | 
					  this->write_register_(PCA6416A_CONFIG1, 0xff);
 | 
				
			||||||
 | 
					  // Read current output register state
 | 
				
			||||||
 | 
					  this->read_register_(PCA6416A_OUTPUT0, &this->output_0_);
 | 
				
			||||||
 | 
					  this->read_register_(PCA6416A_OUTPUT1, &this->output_1_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
 | 
				
			||||||
 | 
					           this->status_has_error());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AComponent::dump_config() {
 | 
				
			||||||
 | 
					  if (this->has_pullup_) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "PCAL6416A:");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "PCA6416A:");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  LOG_I2C_DEVICE(this)
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication with PCA6416A failed!");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PCA6416AComponent::digital_read(uint8_t pin) {
 | 
				
			||||||
 | 
					  uint8_t bit = pin % 8;
 | 
				
			||||||
 | 
					  uint8_t reg_addr = pin < 8 ? PCA6416A_INPUT0 : PCA6416A_INPUT1;
 | 
				
			||||||
 | 
					  uint8_t value = 0;
 | 
				
			||||||
 | 
					  this->read_register_(reg_addr, &value);
 | 
				
			||||||
 | 
					  return value & (1 << bit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AComponent::digital_write(uint8_t pin, bool value) {
 | 
				
			||||||
 | 
					  uint8_t reg_addr = pin < 8 ? PCA6416A_OUTPUT0 : PCA6416A_OUTPUT1;
 | 
				
			||||||
 | 
					  this->update_register_(pin, value, reg_addr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AComponent::pin_mode(uint8_t pin, gpio::Flags flags) {
 | 
				
			||||||
 | 
					  uint8_t io_dir = pin < 8 ? PCA6416A_CONFIG0 : PCA6416A_CONFIG1;
 | 
				
			||||||
 | 
					  uint8_t pull_en = pin < 8 ? PCAL6416A_PULL_EN0 : PCAL6416A_PULL_EN1;
 | 
				
			||||||
 | 
					  uint8_t pull_dir = pin < 8 ? PCAL6416A_PULL_DIR0 : PCAL6416A_PULL_DIR1;
 | 
				
			||||||
 | 
					  if (flags == gpio::FLAG_INPUT) {
 | 
				
			||||||
 | 
					    this->update_register_(pin, true, io_dir);
 | 
				
			||||||
 | 
					    if (has_pullup_) {
 | 
				
			||||||
 | 
					      this->update_register_(pin, true, pull_dir);
 | 
				
			||||||
 | 
					      this->update_register_(pin, false, pull_en);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
 | 
				
			||||||
 | 
					    this->update_register_(pin, true, io_dir);
 | 
				
			||||||
 | 
					    if (has_pullup_) {
 | 
				
			||||||
 | 
					      this->update_register_(pin, true, pull_dir);
 | 
				
			||||||
 | 
					      this->update_register_(pin, true, pull_en);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "Your PCA6416A does not support pull-up resistors");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (flags == gpio::FLAG_OUTPUT) {
 | 
				
			||||||
 | 
					    this->update_register_(pin, false, io_dir);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) {
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Device marked failed");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ((this->last_error_ = this->read_register(reg, value, 1, true)) != esphome::i2c::ERROR_OK) {
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->status_clear_warning();
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PCA6416AComponent::write_register_(uint8_t reg, uint8_t value) {
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Device marked failed");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ((this->last_error_ = this->write_register(reg, &value, 1, true)) != esphome::i2c::ERROR_OK) {
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->status_clear_warning();
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AComponent::update_register_(uint8_t pin, bool pin_value, uint8_t reg_addr) {
 | 
				
			||||||
 | 
					  uint8_t bit = pin % 8;
 | 
				
			||||||
 | 
					  uint8_t reg_value = 0;
 | 
				
			||||||
 | 
					  if (reg_addr == PCA6416A_OUTPUT0) {
 | 
				
			||||||
 | 
					    reg_value = this->output_0_;
 | 
				
			||||||
 | 
					  } else if (reg_addr == PCA6416A_OUTPUT1) {
 | 
				
			||||||
 | 
					    reg_value = this->output_1_;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->read_register_(reg_addr, ®_value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (pin_value) {
 | 
				
			||||||
 | 
					    reg_value |= 1 << bit;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    reg_value &= ~(1 << bit);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->write_register_(reg_addr, reg_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (reg_addr == PCA6416A_OUTPUT0) {
 | 
				
			||||||
 | 
					    this->output_0_ = reg_value;
 | 
				
			||||||
 | 
					  } else if (reg_addr == PCA6416A_OUTPUT1) {
 | 
				
			||||||
 | 
					    this->output_1_ = reg_value;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float PCA6416AComponent::get_setup_priority() const { return setup_priority::IO; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PCA6416AGPIOPin::setup() { pin_mode(flags_); }
 | 
				
			||||||
 | 
					void PCA6416AGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
 | 
				
			||||||
 | 
					bool PCA6416AGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
 | 
				
			||||||
 | 
					void PCA6416AGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
 | 
				
			||||||
 | 
					std::string PCA6416AGPIOPin::dump_summary() const {
 | 
				
			||||||
 | 
					  char buffer[32];
 | 
				
			||||||
 | 
					  snprintf(buffer, sizeof(buffer), "%u via PCA6416A", pin_);
 | 
				
			||||||
 | 
					  return buffer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace pca6416a
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										63
									
								
								esphome/components/pca6416a/pca6416a.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								esphome/components/pca6416a/pca6416a.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/hal.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace pca6416a {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PCA6416AComponent : public Component, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  PCA6416AComponent() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Check i2c availability and setup masks
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  /// Helper function to read the value of a pin.
 | 
				
			||||||
 | 
					  bool digital_read(uint8_t pin);
 | 
				
			||||||
 | 
					  /// Helper function to write the value of a pin.
 | 
				
			||||||
 | 
					  void digital_write(uint8_t pin, bool value);
 | 
				
			||||||
 | 
					  /// Helper function to set the pin mode of a pin.
 | 
				
			||||||
 | 
					  void pin_mode(uint8_t pin, gpio::Flags flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  bool read_register_(uint8_t reg, uint8_t *value);
 | 
				
			||||||
 | 
					  bool write_register_(uint8_t reg, uint8_t value);
 | 
				
			||||||
 | 
					  void update_register_(uint8_t pin, bool pin_value, uint8_t reg_addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The mask to write as output state - 1 means HIGH, 0 means LOW
 | 
				
			||||||
 | 
					  uint8_t output_0_{0x00};
 | 
				
			||||||
 | 
					  uint8_t output_1_{0x00};
 | 
				
			||||||
 | 
					  /// Storage for last I2C error seen
 | 
				
			||||||
 | 
					  esphome::i2c::ErrorCode last_error_;
 | 
				
			||||||
 | 
					  /// Only the PCAL6416A has pull-up resistors
 | 
				
			||||||
 | 
					  bool has_pullup_{false};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Helper class to expose a PCA6416A pin as an internal input GPIO pin.
 | 
				
			||||||
 | 
					class PCA6416AGPIOPin : public GPIOPin {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void pin_mode(gpio::Flags flags) override;
 | 
				
			||||||
 | 
					  bool digital_read() override;
 | 
				
			||||||
 | 
					  void digital_write(bool value) override;
 | 
				
			||||||
 | 
					  std::string dump_summary() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_parent(PCA6416AComponent *parent) { parent_ = parent; }
 | 
				
			||||||
 | 
					  void set_pin(uint8_t pin) { pin_ = pin; }
 | 
				
			||||||
 | 
					  void set_inverted(bool inverted) { inverted_ = inverted; }
 | 
				
			||||||
 | 
					  void set_flags(gpio::Flags flags) { flags_ = flags; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  PCA6416AComponent *parent_;
 | 
				
			||||||
 | 
					  uint8_t pin_;
 | 
				
			||||||
 | 
					  bool inverted_;
 | 
				
			||||||
 | 
					  gpio::Flags flags_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace pca6416a
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -81,7 +81,32 @@ void PN532::setup() {
 | 
				
			|||||||
  this->turn_off_rf_();
 | 
					  this->turn_off_rf_();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool PN532::powerdown() {
 | 
				
			||||||
 | 
					  updates_enabled_ = false;
 | 
				
			||||||
 | 
					  requested_read_ = false;
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Powering down PN532");
 | 
				
			||||||
 | 
					  if (!this->write_command_({PN532_COMMAND_POWERDOWN, 0b10100000})) {  // enable i2c,spi wakeup
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error writing powerdown command to PN532");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  std::vector<uint8_t> response;
 | 
				
			||||||
 | 
					  if (!this->read_response(PN532_COMMAND_POWERDOWN, response)) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error reading PN532 powerdown response");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (response[0] != 0x00) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error on PN532 powerdown: %02x", response[0]);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Powerdown successful");
 | 
				
			||||||
 | 
					  delay(1);
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PN532::update() {
 | 
					void PN532::update() {
 | 
				
			||||||
 | 
					  if (!updates_enabled_)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (auto *obj : this->binary_sensors_)
 | 
					  for (auto *obj : this->binary_sensors_)
 | 
				
			||||||
    obj->on_scan_end();
 | 
					    obj->on_scan_end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ static const uint8_t PN532_COMMAND_SAMCONFIGURATION = 0x14;
 | 
				
			|||||||
static const uint8_t PN532_COMMAND_RFCONFIGURATION = 0x32;
 | 
					static const uint8_t PN532_COMMAND_RFCONFIGURATION = 0x32;
 | 
				
			||||||
static const uint8_t PN532_COMMAND_INDATAEXCHANGE = 0x40;
 | 
					static const uint8_t PN532_COMMAND_INDATAEXCHANGE = 0x40;
 | 
				
			||||||
static const uint8_t PN532_COMMAND_INLISTPASSIVETARGET = 0x4A;
 | 
					static const uint8_t PN532_COMMAND_INLISTPASSIVETARGET = 0x4A;
 | 
				
			||||||
 | 
					static const uint8_t PN532_COMMAND_POWERDOWN = 0x16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PN532BinarySensor;
 | 
					class PN532BinarySensor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,6 +31,7 @@ class PN532 : public PollingComponent {
 | 
				
			|||||||
  float get_setup_priority() const override;
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void loop() override;
 | 
					  void loop() override;
 | 
				
			||||||
 | 
					  void on_shutdown() override { powerdown(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
 | 
					  void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
 | 
				
			||||||
  void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); }
 | 
					  void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); }
 | 
				
			||||||
@@ -45,6 +47,7 @@ class PN532 : public PollingComponent {
 | 
				
			|||||||
  void clean_mode();
 | 
					  void clean_mode();
 | 
				
			||||||
  void format_mode();
 | 
					  void format_mode();
 | 
				
			||||||
  void write_mode(nfc::NdefMessage *message);
 | 
					  void write_mode(nfc::NdefMessage *message);
 | 
				
			||||||
 | 
					  bool powerdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  void turn_off_rf_();
 | 
					  void turn_off_rf_();
 | 
				
			||||||
@@ -79,6 +82,7 @@ class PN532 : public PollingComponent {
 | 
				
			|||||||
  bool write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
 | 
					  bool write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
 | 
				
			||||||
  bool clean_mifare_ultralight_();
 | 
					  bool clean_mifare_ultralight_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool updates_enabled_{true};
 | 
				
			||||||
  bool requested_read_{false};
 | 
					  bool requested_read_{false};
 | 
				
			||||||
  std::vector<PN532BinarySensor *> binary_sensors_;
 | 
					  std::vector<PN532BinarySensor *> binary_sensors_;
 | 
				
			||||||
  std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
 | 
					  std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -791,6 +791,57 @@ async def raw_action(var, config, args):
 | 
				
			|||||||
    cg.add(var.set_carrier_frequency(templ))
 | 
					    cg.add(var.set_carrier_frequency(templ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Drayton
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    DraytonData,
 | 
				
			||||||
 | 
					    DraytonBinarySensor,
 | 
				
			||||||
 | 
					    DraytonTrigger,
 | 
				
			||||||
 | 
					    DraytonAction,
 | 
				
			||||||
 | 
					    DraytonDumper,
 | 
				
			||||||
 | 
					) = declare_protocol("Drayton")
 | 
				
			||||||
 | 
					DRAYTON_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0xFFFF)),
 | 
				
			||||||
 | 
					        cv.Required(CONF_CHANNEL): cv.All(cv.hex_int, cv.Range(min=0, max=0x1F)),
 | 
				
			||||||
 | 
					        cv.Required(CONF_COMMAND): cv.All(cv.hex_int, cv.Range(min=0, max=0x7F)),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_binary_sensor("drayton", DraytonBinarySensor, DRAYTON_SCHEMA)
 | 
				
			||||||
 | 
					def drayton_binary_sensor(var, config):
 | 
				
			||||||
 | 
					    cg.add(
 | 
				
			||||||
 | 
					        var.set_data(
 | 
				
			||||||
 | 
					            cg.StructInitializer(
 | 
				
			||||||
 | 
					                DraytonData,
 | 
				
			||||||
 | 
					                ("address", config[CONF_ADDRESS]),
 | 
				
			||||||
 | 
					                ("channel", config[CONF_CHANNEL]),
 | 
				
			||||||
 | 
					                ("command", config[CONF_COMMAND]),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_trigger("drayton", DraytonTrigger, DraytonData)
 | 
				
			||||||
 | 
					def drayton_trigger(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_dumper("drayton", DraytonDumper)
 | 
				
			||||||
 | 
					def drayton_dumper(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_action("drayton", DraytonAction, DRAYTON_SCHEMA)
 | 
				
			||||||
 | 
					async def drayton_action(var, config, args):
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16)
 | 
				
			||||||
 | 
					    cg.add(var.set_address(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_channel(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_command(template_))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# RC5
 | 
					# RC5
 | 
				
			||||||
RC5Data, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol("RC5")
 | 
					RC5Data, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol("RC5")
 | 
				
			||||||
RC5_SCHEMA = cv.Schema(
 | 
					RC5_SCHEMA = cv.Schema(
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										213
									
								
								esphome/components/remote_base/drayton_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								esphome/components/remote_base/drayton_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,213 @@
 | 
				
			|||||||
 | 
					#include "drayton_protocol.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace remote_base {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "remote.drayton";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint32_t BIT_TIME_US = 500;
 | 
				
			||||||
 | 
					static const uint8_t CARRIER_KHZ = 2;
 | 
				
			||||||
 | 
					static const uint8_t NBITS_PREAMBLE = 12;
 | 
				
			||||||
 | 
					static const uint8_t NBITS_SYNC = 4;
 | 
				
			||||||
 | 
					static const uint8_t NBITS_ADDRESS = 16;
 | 
				
			||||||
 | 
					static const uint8_t NBITS_CHANNEL = 5;
 | 
				
			||||||
 | 
					static const uint8_t NBITS_COMMAND = 7;
 | 
				
			||||||
 | 
					static const uint8_t NBITS = NBITS_ADDRESS + NBITS_CHANNEL + NBITS_COMMAND;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint8_t CMD_ON = 0x41;
 | 
				
			||||||
 | 
					static const uint8_t CMD_OFF = 0x02;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Drayton Protocol
 | 
				
			||||||
 | 
					Using an oscilloscope to capture the data transmitted by the Digistat two
 | 
				
			||||||
 | 
					distinct packets for 'On' and 'Off' are transmitted. Each transmitted bit
 | 
				
			||||||
 | 
					has a period of 500us, a bit rate of 2000 baud.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Each packet consists of an initial 1010 pattern to set up the receiver bias.
 | 
				
			||||||
 | 
					The number of these bits seen at the receiver varies depending on the state
 | 
				
			||||||
 | 
					of the bias when the packet transmission starts. The receiver algoritmn takes
 | 
				
			||||||
 | 
					account of this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The packet appears to be Manchester encoded, with a '10' tranmitted pair
 | 
				
			||||||
 | 
					representing a '1' bit and a '01' pair representing a '0' bit. Each packet is
 | 
				
			||||||
 | 
					begun with a '1100' syncronisation symbol which breaks this rule. Following
 | 
				
			||||||
 | 
					the sync are 28 '01' or '10' pairs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Boiler On Command as received:
 | 
				
			||||||
 | 
					101010101010110001101001010101101001010101010101100101010101101001011001
 | 
				
			||||||
 | 
					ppppppppppppSSSS-0-1-1-0-0-0-0-1-1-0-0-0-0-0-0-0-1-0-0-0-0-0-1-1-0-0-1-0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(Where pppp represents the preamble bits and SSSS represents the sync symbol)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					28 bits of data received 01100001100000001000001 10010 (bin) or 6180832 (hex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Boiler Off Command as received:
 | 
				
			||||||
 | 
					101010101010110001101001010101101001010101010101010101010110011001011001
 | 
				
			||||||
 | 
					ppppppppppppSSSS-0-1-1-0-0-0-0-1-1-0-0-0-0-0-0-0-0-0-0-0-0-1-0-1-0-0-1-0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					28 bits of data received 0110000110000000000001010010 (bin) or 6180052 (hex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I have used 'RFLink' software (RLink Firmware Version: 1.1 Revision: 48) to
 | 
				
			||||||
 | 
					capture and retransmit the Digistat packets. RFLink splits each packet into an
 | 
				
			||||||
 | 
					ID, SWITCH, and CMD field.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0;17;Drayton;ID=c300;SWITCH=12;CMD=ON;
 | 
				
			||||||
 | 
					20;18;Drayton;ID=c300;SWITCH=12;CMD=OFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Spliting my received data into three parts of 16, 7 and 5 bits gives address,
 | 
				
			||||||
 | 
					channel and Command values of:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On  6180832  0110000110000000 1000001 10010
 | 
				
			||||||
 | 
					address: '0x6180' channel: '0x12' command: '0x41'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Off 6180052  0110000110000000 0000010 10010
 | 
				
			||||||
 | 
					address: '0x6180' channel: '0x12' command: '0x02'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These values are slightly different to those used by RFLink (the RFLink
 | 
				
			||||||
 | 
					ID/Adress value is rotated/manipulated), and I don't know who's interpretation
 | 
				
			||||||
 | 
					is correct. A larger data sample would help (I have only found five different
 | 
				
			||||||
 | 
					packet captures online) or definitive information from Drayton.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Splitting each packet in this way works well for me with esphome. Any
 | 
				
			||||||
 | 
					corrections or additional data samples would be gratefully received.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					marshn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DraytonProtocol::encode(RemoteTransmitData *dst, const DraytonData &data) {
 | 
				
			||||||
 | 
					  uint16_t khz = CARRIER_KHZ;
 | 
				
			||||||
 | 
					  dst->set_carrier_frequency(khz * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Preamble = 101010101010
 | 
				
			||||||
 | 
					  uint32_t out_data = 0x0AAA;
 | 
				
			||||||
 | 
					  for (uint32_t mask = 1UL << (NBITS_PREAMBLE - 1); mask != 0; mask >>= 1) {
 | 
				
			||||||
 | 
					    if (out_data & mask) {
 | 
				
			||||||
 | 
					      dst->mark(BIT_TIME_US);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dst->space(BIT_TIME_US);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Sync = 1100
 | 
				
			||||||
 | 
					  out_data = 0x000C;
 | 
				
			||||||
 | 
					  for (uint32_t mask = 1UL << (NBITS_SYNC - 1); mask != 0; mask >>= 1) {
 | 
				
			||||||
 | 
					    if (out_data & mask) {
 | 
				
			||||||
 | 
					      dst->mark(BIT_TIME_US);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dst->space(BIT_TIME_US);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Send Drayton: address=%04x channel=%03x cmd=%02x", data.address, data.channel, data.command);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out_data = data.address;
 | 
				
			||||||
 | 
					  out_data <<= NBITS_COMMAND;
 | 
				
			||||||
 | 
					  out_data |= data.command;
 | 
				
			||||||
 | 
					  out_data <<= NBITS_CHANNEL;
 | 
				
			||||||
 | 
					  out_data |= data.channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Send Drayton: out_data %08x", out_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) {
 | 
				
			||||||
 | 
					    if (out_data & mask) {
 | 
				
			||||||
 | 
					      dst->mark(BIT_TIME_US);
 | 
				
			||||||
 | 
					      dst->space(BIT_TIME_US);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dst->space(BIT_TIME_US);
 | 
				
			||||||
 | 
					      dst->mark(BIT_TIME_US);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
 | 
				
			||||||
 | 
					  DraytonData out{
 | 
				
			||||||
 | 
					      .address = 0,
 | 
				
			||||||
 | 
					      .channel = 0,
 | 
				
			||||||
 | 
					      .command = 0,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (src.size() < 45) {
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGVV(TAG, "Decode Drayton: %d, %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(),
 | 
				
			||||||
 | 
					            src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7),
 | 
				
			||||||
 | 
					            src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
 | 
				
			||||||
 | 
					            src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If first preamble item is a space, skip it
 | 
				
			||||||
 | 
					  if (src.peek_space_at_least(1)) {
 | 
				
			||||||
 | 
					    src.advance(1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Look for sync pulse, after. If sucessful index points to space of sync symbol
 | 
				
			||||||
 | 
					  for (uint16_t preamble = 0; preamble <= NBITS_PREAMBLE * 2; preamble += 2) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "Decode Drayton: preamble %d  %d %d", preamble, src.peek(preamble), src.peek(preamble + 1));
 | 
				
			||||||
 | 
					    if (src.peek_mark(2 * BIT_TIME_US, preamble) &&
 | 
				
			||||||
 | 
					        (src.peek_space(2 * BIT_TIME_US, preamble + 1) || src.peek_space(3 * BIT_TIME_US, preamble + 1))) {
 | 
				
			||||||
 | 
					      src.advance(preamble + 1);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Read data. Index points to space of sync symbol
 | 
				
			||||||
 | 
					  // Extract first bit
 | 
				
			||||||
 | 
					  // Checks next bit to leave index pointing correctly
 | 
				
			||||||
 | 
					  uint32_t out_data = 0;
 | 
				
			||||||
 | 
					  uint8_t bit = NBITS_ADDRESS + NBITS_COMMAND + NBITS_CHANNEL - 1;
 | 
				
			||||||
 | 
					  if (src.expect_space(3 * BIT_TIME_US) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) {
 | 
				
			||||||
 | 
					    out_data |= 0 << bit;
 | 
				
			||||||
 | 
					  } else if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US) &&
 | 
				
			||||||
 | 
					             (src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) {
 | 
				
			||||||
 | 
					    out_data |= 1 << bit;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Decode Drayton: Fail 1, - %d", src.get_index());
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Before/after each bit is read the index points to the transition at the start of the bit period or,
 | 
				
			||||||
 | 
					  // if there is no transition at the start of the bit period, then the transition in the middle of
 | 
				
			||||||
 | 
					  // the previous bit period.
 | 
				
			||||||
 | 
					  while (--bit >= 1) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "Decode Drayton: Data, %2d %08x", bit, out_data);
 | 
				
			||||||
 | 
					    if ((src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) &&
 | 
				
			||||||
 | 
					        (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) {
 | 
				
			||||||
 | 
					      out_data |= 0 << bit;
 | 
				
			||||||
 | 
					    } else if ((src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) &&
 | 
				
			||||||
 | 
					               (src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) {
 | 
				
			||||||
 | 
					      out_data |= 1 << bit;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      ESP_LOGVV(TAG, "Decode Drayton: Fail 2, %2d %08x", bit, out_data);
 | 
				
			||||||
 | 
					      return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) {
 | 
				
			||||||
 | 
					    out_data |= 0;
 | 
				
			||||||
 | 
					  } else if (src.expect_mark(BIT_TIME_US) || src.expect_mark(2 * BIT_TIME_US)) {
 | 
				
			||||||
 | 
					    out_data |= 1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Decode Drayton: Data, %2d %08x", bit, out_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.channel = (uint8_t) (out_data & 0x1F);
 | 
				
			||||||
 | 
					  out_data >>= NBITS_CHANNEL;
 | 
				
			||||||
 | 
					  out.command = (uint8_t) (out_data & 0x7F);
 | 
				
			||||||
 | 
					  out_data >>= NBITS_COMMAND;
 | 
				
			||||||
 | 
					  out.address = (uint16_t) (out_data & 0xFFFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void DraytonProtocol::dump(const DraytonData &data) {
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Received Drayton: address=0x%04X (0x%04x), channel=0x%03x command=0x%03X", data.address,
 | 
				
			||||||
 | 
					           ((data.address << 1) & 0xffff), data.channel, data.command);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace remote_base
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										44
									
								
								esphome/components/remote_base/drayton_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/remote_base/drayton_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "remote_base.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace remote_base {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DraytonData {
 | 
				
			||||||
 | 
					  uint16_t address;
 | 
				
			||||||
 | 
					  uint8_t channel;
 | 
				
			||||||
 | 
					  uint8_t command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool operator==(const DraytonData &rhs) const {
 | 
				
			||||||
 | 
					    return address == rhs.address && channel == rhs.channel && command == rhs.command;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DraytonProtocol : public RemoteProtocol<DraytonData> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void encode(RemoteTransmitData *dst, const DraytonData &data) override;
 | 
				
			||||||
 | 
					  optional<DraytonData> decode(RemoteReceiveData src) override;
 | 
				
			||||||
 | 
					  void dump(const DraytonData &data) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DECLARE_REMOTE_PROTOCOL(Drayton)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class DraytonAction : public RemoteTransmitterActionBase<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint16_t, address)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, channel)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void encode(RemoteTransmitData *dst, Ts... x) override {
 | 
				
			||||||
 | 
					    DraytonData data{};
 | 
				
			||||||
 | 
					    data.address = this->address_.value(x...);
 | 
				
			||||||
 | 
					    data.channel = this->channel_.value(x...);
 | 
				
			||||||
 | 
					    data.command = this->command_.value(x...);
 | 
				
			||||||
 | 
					    DraytonProtocol().encode(dst, data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace remote_base
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -286,7 +286,9 @@ SPRINKLER_VALVE_SCHEMA = cv.Schema(
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        cv.Optional(CONF_ENABLE_SWITCH): cv.maybe_simple_value(
 | 
					        cv.Optional(CONF_ENABLE_SWITCH): cv.maybe_simple_value(
 | 
				
			||||||
            switch.switch_schema(
 | 
					            switch.switch_schema(
 | 
				
			||||||
                SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
 | 
					                SprinklerControllerSwitch,
 | 
				
			||||||
 | 
					                entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					                default_restore_mode="RESTORE_DEFAULT_OFF",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            key=CONF_NAME,
 | 
					            key=CONF_NAME,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@@ -333,7 +335,9 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema(
 | 
				
			|||||||
        cv.Optional(CONF_NAME): cv.string,
 | 
					        cv.Optional(CONF_NAME): cv.string,
 | 
				
			||||||
        cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value(
 | 
					        cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value(
 | 
				
			||||||
            switch.switch_schema(
 | 
					            switch.switch_schema(
 | 
				
			||||||
                SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
 | 
					                SprinklerControllerSwitch,
 | 
				
			||||||
 | 
					                entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					                default_restore_mode="RESTORE_DEFAULT_OFF",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            key=CONF_NAME,
 | 
					            key=CONF_NAME,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@@ -343,19 +347,25 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema(
 | 
				
			|||||||
        ),
 | 
					        ),
 | 
				
			||||||
        cv.Optional(CONF_QUEUE_ENABLE_SWITCH): cv.maybe_simple_value(
 | 
					        cv.Optional(CONF_QUEUE_ENABLE_SWITCH): cv.maybe_simple_value(
 | 
				
			||||||
            switch.switch_schema(
 | 
					            switch.switch_schema(
 | 
				
			||||||
                SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
 | 
					                SprinklerControllerSwitch,
 | 
				
			||||||
 | 
					                entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					                default_restore_mode="RESTORE_DEFAULT_OFF",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            key=CONF_NAME,
 | 
					            key=CONF_NAME,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        cv.Optional(CONF_REVERSE_SWITCH): cv.maybe_simple_value(
 | 
					        cv.Optional(CONF_REVERSE_SWITCH): cv.maybe_simple_value(
 | 
				
			||||||
            switch.switch_schema(
 | 
					            switch.switch_schema(
 | 
				
			||||||
                SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
 | 
					                SprinklerControllerSwitch,
 | 
				
			||||||
 | 
					                entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					                default_restore_mode="RESTORE_DEFAULT_OFF",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            key=CONF_NAME,
 | 
					            key=CONF_NAME,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        cv.Optional(CONF_STANDBY_SWITCH): cv.maybe_simple_value(
 | 
					        cv.Optional(CONF_STANDBY_SWITCH): cv.maybe_simple_value(
 | 
				
			||||||
            switch.switch_schema(
 | 
					            switch.switch_schema(
 | 
				
			||||||
                SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
 | 
					                SprinklerControllerSwitch,
 | 
				
			||||||
 | 
					                entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					                default_restore_mode="RESTORE_DEFAULT_OFF",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            key=CONF_NAME,
 | 
					            key=CONF_NAME,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1176,6 +1176,21 @@ optional<uint32_t> Sprinkler::time_remaining_current_operation() {
 | 
				
			|||||||
  return nullopt;
 | 
					  return nullopt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Sprinkler::any_controller_is_active() {
 | 
				
			||||||
 | 
					  if (this->state_ != IDLE) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto &controller : this->other_controllers_) {
 | 
				
			||||||
 | 
					    if (controller != this) {  // dummy check
 | 
				
			||||||
 | 
					      if (controller->controller_state() != IDLE) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SprinklerControllerSwitch *Sprinkler::control_switch(size_t valve_number) {
 | 
					SprinklerControllerSwitch *Sprinkler::control_switch(size_t valve_number) {
 | 
				
			||||||
  if (this->is_a_valid_valve(valve_number)) {
 | 
					  if (this->is_a_valid_valve(valve_number)) {
 | 
				
			||||||
    return this->valve_[valve_number].controller_switch;
 | 
					    return this->valve_[valve_number].controller_switch;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -406,6 +406,12 @@ class Sprinkler : public Component {
 | 
				
			|||||||
  /// returns the amount of time remaining in seconds for all valves remaining, including the active valve, if any
 | 
					  /// returns the amount of time remaining in seconds for all valves remaining, including the active valve, if any
 | 
				
			||||||
  optional<uint32_t> time_remaining_current_operation();
 | 
					  optional<uint32_t> time_remaining_current_operation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// returns true if this or any sprinkler controller this controller knows about is active
 | 
				
			||||||
 | 
					  bool any_controller_is_active();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// returns the current state of the sprinkler controller
 | 
				
			||||||
 | 
					  SprinklerState controller_state() { return this->state_; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// returns a pointer to a valve's control switch object
 | 
					  /// returns a pointer to a valve's control switch object
 | 
				
			||||||
  SprinklerControllerSwitch *control_switch(size_t valve_number);
 | 
					  SprinklerControllerSwitch *control_switch(size_t valve_number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -503,7 +509,6 @@ class Sprinkler : public Component {
 | 
				
			|||||||
  /// callback functions for timers
 | 
					  /// callback functions for timers
 | 
				
			||||||
  void valve_selection_callback_();
 | 
					  void valve_selection_callback_();
 | 
				
			||||||
  void sm_timer_callback_();
 | 
					  void sm_timer_callback_();
 | 
				
			||||||
  void pump_stop_delay_callback_();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Maximum allowed queue size
 | 
					  /// Maximum allowed queue size
 | 
				
			||||||
  const uint8_t max_queue_size_{100};
 | 
					  const uint8_t max_queue_size_{100};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,9 @@ WaveshareEPaper4P2InBV2 = waveshare_epaper_ns.class_(
 | 
				
			|||||||
WaveshareEPaper5P8In = waveshare_epaper_ns.class_(
 | 
					WaveshareEPaper5P8In = waveshare_epaper_ns.class_(
 | 
				
			||||||
    "WaveshareEPaper5P8In", WaveshareEPaper
 | 
					    "WaveshareEPaper5P8In", WaveshareEPaper
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					WaveshareEPaper5P8InV2 = waveshare_epaper_ns.class_(
 | 
				
			||||||
 | 
					    "WaveshareEPaper5P8InV2", WaveshareEPaper
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
WaveshareEPaper7P5In = waveshare_epaper_ns.class_(
 | 
					WaveshareEPaper7P5In = waveshare_epaper_ns.class_(
 | 
				
			||||||
    "WaveshareEPaper7P5In", WaveshareEPaper
 | 
					    "WaveshareEPaper7P5In", WaveshareEPaper
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -80,6 +83,7 @@ MODELS = {
 | 
				
			|||||||
    "4.20in": ("b", WaveshareEPaper4P2In),
 | 
					    "4.20in": ("b", WaveshareEPaper4P2In),
 | 
				
			||||||
    "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),
 | 
					    "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),
 | 
				
			||||||
    "5.83in": ("b", WaveshareEPaper5P8In),
 | 
					    "5.83in": ("b", WaveshareEPaper5P8In),
 | 
				
			||||||
 | 
					    "5.83inv2": ("b", WaveshareEPaper5P8InV2),
 | 
				
			||||||
    "7.50in": ("b", WaveshareEPaper7P5In),
 | 
					    "7.50in": ("b", WaveshareEPaper7P5In),
 | 
				
			||||||
    "7.50in-bv2": ("b", WaveshareEPaper7P5InBV2),
 | 
					    "7.50in-bv2": ("b", WaveshareEPaper7P5InBV2),
 | 
				
			||||||
    "7.50in-bc": ("b", WaveshareEPaper7P5InBC),
 | 
					    "7.50in-bc": ("b", WaveshareEPaper7P5InBC),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1037,6 +1037,88 @@ void WaveshareEPaper5P8In::dump_config() {
 | 
				
			|||||||
  LOG_PIN("  Busy Pin: ", this->busy_pin_);
 | 
					  LOG_PIN("  Busy Pin: ", this->busy_pin_);
 | 
				
			||||||
  LOG_UPDATE_INTERVAL(this);
 | 
					  LOG_UPDATE_INTERVAL(this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ========================================================
 | 
				
			||||||
 | 
					//               5.83in V2
 | 
				
			||||||
 | 
					// Datasheet/Specification/Reference:
 | 
				
			||||||
 | 
					//  - https://www.waveshare.com/w/upload/3/37/5.83inch_e-Paper_V2_Specification.pdf
 | 
				
			||||||
 | 
					//  - https://github.com/waveshare/e-Paper/blob/master/Arduino/epd5in83_V2/epd5in83_V2.cpp
 | 
				
			||||||
 | 
					// ========================================================
 | 
				
			||||||
 | 
					void WaveshareEPaper5P8InV2::initialize() {
 | 
				
			||||||
 | 
					  // COMMAND POWER SETTING
 | 
				
			||||||
 | 
					  this->command(0x01);
 | 
				
			||||||
 | 
					  this->data(0x07);
 | 
				
			||||||
 | 
					  this->data(0x07);
 | 
				
			||||||
 | 
					  this->data(0x3f);
 | 
				
			||||||
 | 
					  this->data(0x3f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND POWER ON
 | 
				
			||||||
 | 
					  this->command(0x04);
 | 
				
			||||||
 | 
					  delay(10);
 | 
				
			||||||
 | 
					  this->wait_until_idle_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PANNEL SETTING
 | 
				
			||||||
 | 
					  this->command(0x00);
 | 
				
			||||||
 | 
					  this->data(0x1F);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND RESOLUTION SETTING
 | 
				
			||||||
 | 
					  this->command(0x61);
 | 
				
			||||||
 | 
					  this->data(0x02);
 | 
				
			||||||
 | 
					  this->data(0x88);
 | 
				
			||||||
 | 
					  this->data(0x01);
 | 
				
			||||||
 | 
					  this->data(0xE0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->command(0x15);
 | 
				
			||||||
 | 
					  this->data(0x00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND TCON SETTING
 | 
				
			||||||
 | 
					  this->command(0x60);
 | 
				
			||||||
 | 
					  this->data(0x22);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Do we need this?
 | 
				
			||||||
 | 
					  // COMMAND PLL CONTROL
 | 
				
			||||||
 | 
					  this->command(0x30);
 | 
				
			||||||
 | 
					  this->data(0x3C);  // 3A 100HZ   29 150Hz 39 200HZ  31 171HZ
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void HOT WaveshareEPaper5P8InV2::display() {
 | 
				
			||||||
 | 
					  // Reuse the code from WaveshareEPaper4P2In::display()
 | 
				
			||||||
 | 
					  // COMMAND VCM DC SETTING REGISTER
 | 
				
			||||||
 | 
					  this->command(0x82);
 | 
				
			||||||
 | 
					  this->data(0x12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND VCOM AND DATA INTERVAL SETTING
 | 
				
			||||||
 | 
					  this->command(0x50);
 | 
				
			||||||
 | 
					  this->data(0x97);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND DATA START TRANSMISSION 1
 | 
				
			||||||
 | 
					  this->command(0x10);
 | 
				
			||||||
 | 
					  delay(2);
 | 
				
			||||||
 | 
					  this->start_data_();
 | 
				
			||||||
 | 
					  this->write_array(this->buffer_, this->get_buffer_length_());
 | 
				
			||||||
 | 
					  this->end_data_();
 | 
				
			||||||
 | 
					  delay(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND DATA START TRANSMISSION 2
 | 
				
			||||||
 | 
					  this->command(0x13);
 | 
				
			||||||
 | 
					  delay(2);
 | 
				
			||||||
 | 
					  this->start_data_();
 | 
				
			||||||
 | 
					  this->write_array(this->buffer_, this->get_buffer_length_());
 | 
				
			||||||
 | 
					  this->end_data_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // COMMAND DISPLAY REFRESH
 | 
				
			||||||
 | 
					  this->command(0x12);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					int WaveshareEPaper5P8InV2::get_width_internal() { return 648; }
 | 
				
			||||||
 | 
					int WaveshareEPaper5P8InV2::get_height_internal() { return 480; }
 | 
				
			||||||
 | 
					void WaveshareEPaper5P8InV2::dump_config() {
 | 
				
			||||||
 | 
					  LOG_DISPLAY("", "Waveshare E-Paper", this);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Model: 5.83inv2");
 | 
				
			||||||
 | 
					  LOG_PIN("  Reset Pin: ", this->reset_pin_);
 | 
				
			||||||
 | 
					  LOG_PIN("  DC Pin: ", this->dc_pin_);
 | 
				
			||||||
 | 
					  LOG_PIN("  Busy Pin: ", this->busy_pin_);
 | 
				
			||||||
 | 
					  LOG_UPDATE_INTERVAL(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void WaveshareEPaper7P5InBV2::initialize() {
 | 
					void WaveshareEPaper7P5InBV2::initialize() {
 | 
				
			||||||
  // COMMAND POWER SETTING
 | 
					  // COMMAND POWER SETTING
 | 
				
			||||||
  this->command(0x01);
 | 
					  this->command(0x01);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -284,6 +284,49 @@ class WaveshareEPaper5P8In : public WaveshareEPaper {
 | 
				
			|||||||
  int get_height_internal() override;
 | 
					  int get_height_internal() override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WaveshareEPaper5P8InV2 : public WaveshareEPaper {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void initialize() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void display() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void deep_sleep() override {
 | 
				
			||||||
 | 
					    // COMMAND VCOM AND DATA INTERVAL SETTING
 | 
				
			||||||
 | 
					    this->command(0x50);
 | 
				
			||||||
 | 
					    this->data(0x17);  // border floating
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // COMMAND VCM DC SETTING
 | 
				
			||||||
 | 
					    this->command(0x82);
 | 
				
			||||||
 | 
					    // COMMAND PANEL SETTING
 | 
				
			||||||
 | 
					    this->command(0x00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    delay(100);  // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // COMMAND POWER SETTING
 | 
				
			||||||
 | 
					    this->command(0x01);
 | 
				
			||||||
 | 
					    this->data(0x00);
 | 
				
			||||||
 | 
					    this->data(0x00);
 | 
				
			||||||
 | 
					    this->data(0x00);
 | 
				
			||||||
 | 
					    this->data(0x00);
 | 
				
			||||||
 | 
					    this->data(0x00);
 | 
				
			||||||
 | 
					    delay(100);  // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // COMMAND POWER OFF
 | 
				
			||||||
 | 
					    this->command(0x02);
 | 
				
			||||||
 | 
					    this->wait_until_idle_();
 | 
				
			||||||
 | 
					    // COMMAND DEEP SLEEP
 | 
				
			||||||
 | 
					    this->command(0x07);
 | 
				
			||||||
 | 
					    this->data(0xA5);  // check byte
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  int get_width_internal() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int get_height_internal() override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WaveshareEPaper7P5In : public WaveshareEPaper {
 | 
					class WaveshareEPaper7P5In : public WaveshareEPaper {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  void initialize() override;
 | 
					  void initialize() override;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -428,6 +428,9 @@ void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
 | 
				
			|||||||
std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
 | 
					std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
 | 
				
			||||||
  return json::build_json([obj, value, start_config](JsonObject root) {
 | 
					  return json::build_json([obj, value, start_config](JsonObject root) {
 | 
				
			||||||
    set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
 | 
					    set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
 | 
				
			||||||
 | 
					    if (start_config == DETAIL_ALL) {
 | 
				
			||||||
 | 
					      root["assumed_state"] = obj->assumed_state();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
 | 
					void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,7 @@ lib_deps =
 | 
				
			|||||||
    ${common:arduino.lib_deps}
 | 
					    ${common:arduino.lib_deps}
 | 
				
			||||||
    ESP8266WiFi                           ; wifi (Arduino built-in)
 | 
					    ESP8266WiFi                           ; wifi (Arduino built-in)
 | 
				
			||||||
    Update                                ; ota (Arduino built-in)
 | 
					    Update                                ; ota (Arduino built-in)
 | 
				
			||||||
    ottowinter/ESPAsyncTCP-esphome@1.2.3  ; async_tcp
 | 
					    esphome/ESPAsyncTCP-esphome@1.2.3  ; async_tcp
 | 
				
			||||||
    ESP8266HTTPClient                     ; http_request (Arduino built-in)
 | 
					    ESP8266HTTPClient                     ; http_request (Arduino built-in)
 | 
				
			||||||
    ESP8266mDNS                           ; mdns (Arduino built-in)
 | 
					    ESP8266mDNS                           ; mdns (Arduino built-in)
 | 
				
			||||||
    DNSServer                             ; captive_portal (Arduino built-in)
 | 
					    DNSServer                             ; captive_portal (Arduino built-in)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,8 @@ platformio==6.1.6  # When updating platformio, also update Dockerfile
 | 
				
			|||||||
esptool==4.5.1
 | 
					esptool==4.5.1
 | 
				
			||||||
click==8.1.3
 | 
					click==8.1.3
 | 
				
			||||||
esphome-dashboard==20230214.0
 | 
					esphome-dashboard==20230214.0
 | 
				
			||||||
aioesphomeapi==13.7.1
 | 
					aioesphomeapi==13.7.2
 | 
				
			||||||
zeroconf==0.56.0
 | 
					zeroconf==0.60.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# esp-idf requires this, but doesn't bundle it by default
 | 
					# esp-idf requires this, but doesn't bundle it by default
 | 
				
			||||||
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24
 | 
					# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
pylint==2.17.3
 | 
					pylint==2.17.3
 | 
				
			||||||
flake8==6.0.0  # also change in .pre-commit-config.yaml when updating
 | 
					flake8==6.0.0  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
black==23.3.0  # also change in .pre-commit-config.yaml when updating
 | 
					black==23.3.0  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
pyupgrade==3.3.1  # also change in .pre-commit-config.yaml when updating
 | 
					pyupgrade==3.3.2  # also change in .pre-commit-config.yaml when updating
 | 
				
			||||||
pre-commit
 | 
					pre-commit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Unit tests
 | 
					# Unit tests
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,22 @@
 | 
				
			|||||||
# This script is used to preinstall
 | 
					# This script is used to preinstall
 | 
				
			||||||
# all platformio libraries in the global storage
 | 
					# all platformio libraries in the global storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import argparse
 | 
				
			||||||
import configparser
 | 
					import configparser
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
config = configparser.ConfigParser(inline_comment_prefixes=(";",))
 | 
					config = configparser.ConfigParser(inline_comment_prefixes=(";",))
 | 
				
			||||||
config.read(sys.argv[1])
 | 
					
 | 
				
			||||||
 | 
					parser = argparse.ArgumentParser(description="")
 | 
				
			||||||
 | 
					parser.add_argument("file", help="Path to platformio.ini", nargs=1)
 | 
				
			||||||
 | 
					parser.add_argument("-l", "--libraries", help="Install libraries", action="store_true")
 | 
				
			||||||
 | 
					parser.add_argument("-p", "--platforms", help="Install platforms", action="store_true")
 | 
				
			||||||
 | 
					parser.add_argument("-t", "--tools", help="Install tools", action="store_true")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					args = parser.parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config.read(args.file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libs = []
 | 
					libs = []
 | 
				
			||||||
tools = []
 | 
					tools = []
 | 
				
			||||||
@@ -15,7 +25,7 @@ platforms = []
 | 
				
			|||||||
# Extract from every lib_deps key in all sections
 | 
					# Extract from every lib_deps key in all sections
 | 
				
			||||||
for section in config.sections():
 | 
					for section in config.sections():
 | 
				
			||||||
    conf = config[section]
 | 
					    conf = config[section]
 | 
				
			||||||
    if "lib_deps" in conf:
 | 
					    if "lib_deps" in conf and args.libraries:
 | 
				
			||||||
        for lib_dep in conf["lib_deps"].splitlines():
 | 
					        for lib_dep in conf["lib_deps"].splitlines():
 | 
				
			||||||
            if not lib_dep:
 | 
					            if not lib_dep:
 | 
				
			||||||
                # Empty line or comment
 | 
					                # Empty line or comment
 | 
				
			||||||
@@ -28,10 +38,10 @@ for section in config.sections():
 | 
				
			|||||||
                continue
 | 
					                continue
 | 
				
			||||||
            libs.append("-l")
 | 
					            libs.append("-l")
 | 
				
			||||||
            libs.append(lib_dep)
 | 
					            libs.append(lib_dep)
 | 
				
			||||||
    if "platform" in conf:
 | 
					    if "platform" in conf and args.platforms:
 | 
				
			||||||
        platforms.append("-p")
 | 
					        platforms.append("-p")
 | 
				
			||||||
        platforms.append(conf["platform"])
 | 
					        platforms.append(conf["platform"])
 | 
				
			||||||
    if "platform_packages" in conf:
 | 
					    if "platform_packages" in conf and args.tools:
 | 
				
			||||||
        for tool in conf["platform_packages"].splitlines():
 | 
					        for tool in conf["platform_packages"].splitlines():
 | 
				
			||||||
            if not tool:
 | 
					            if not tool:
 | 
				
			||||||
                # Empty line or comment
 | 
					                # Empty line or comment
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,4 +15,4 @@ pip3 install --no-use-pep517 -e .
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pre-commit install
 | 
					pre-commit install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
script/platformio_install_deps.py platformio.ini
 | 
					script/platformio_install_deps.py platformio.ini --libraries --tools --platforms
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -294,6 +294,9 @@ wled:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
adalight:
 | 
					adalight:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					esp32_ble:
 | 
				
			||||||
 | 
					  io_capability: keyboard_only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
esp32_ble_tracker:
 | 
					esp32_ble_tracker:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ble_client:
 | 
					ble_client:
 | 
				
			||||||
@@ -307,6 +310,19 @@ ble_client:
 | 
				
			|||||||
    on_disconnect:
 | 
					    on_disconnect:
 | 
				
			||||||
      then:
 | 
					      then:
 | 
				
			||||||
        - switch.turn_on: ble1_status
 | 
					        - switch.turn_on: ble1_status
 | 
				
			||||||
 | 
					    on_passkey_request:
 | 
				
			||||||
 | 
					      then:
 | 
				
			||||||
 | 
					        - ble_client.passkey_reply:
 | 
				
			||||||
 | 
					            id: ble_blah
 | 
				
			||||||
 | 
					            passkey: 123456
 | 
				
			||||||
 | 
					    on_passkey_notification:
 | 
				
			||||||
 | 
					      then:
 | 
				
			||||||
 | 
					        - logger.log: "Passkey notification received"
 | 
				
			||||||
 | 
					    on_numeric_comparison_request:
 | 
				
			||||||
 | 
					      then:
 | 
				
			||||||
 | 
					        - ble_client.numeric_comparison_reply:
 | 
				
			||||||
 | 
					            id: ble_blah
 | 
				
			||||||
 | 
					            accept: True
 | 
				
			||||||
  - mac_address: C4:4F:33:11:22:33
 | 
					  - mac_address: C4:4F:33:11:22:33
 | 
				
			||||||
    id: my_bedjet_ble_client
 | 
					    id: my_bedjet_ble_client
 | 
				
			||||||
bedjet:
 | 
					bedjet:
 | 
				
			||||||
@@ -1276,6 +1292,13 @@ sensor:
 | 
				
			|||||||
    name: DHT Absolute Humidity
 | 
					    name: DHT Absolute Humidity
 | 
				
			||||||
    temperature: dht_temperature
 | 
					    temperature: dht_temperature
 | 
				
			||||||
    humidity: dht_humidity
 | 
					    humidity: dht_humidity
 | 
				
			||||||
 | 
					  - platform: hyt271
 | 
				
			||||||
 | 
					    i2c_id: i2c_bus
 | 
				
			||||||
 | 
					    temperature:
 | 
				
			||||||
 | 
					      name: "Temperature hyt271"
 | 
				
			||||||
 | 
					      id: temp_etuve
 | 
				
			||||||
 | 
					    humidity:
 | 
				
			||||||
 | 
					      name: "Humidity hyt271"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
esp32_touch:
 | 
					esp32_touch:
 | 
				
			||||||
  setup_mode: false
 | 
					  setup_mode: false
 | 
				
			||||||
@@ -1449,6 +1472,13 @@ binary_sensor:
 | 
				
			|||||||
      number: 1
 | 
					      number: 1
 | 
				
			||||||
      mode: INPUT
 | 
					      mode: INPUT
 | 
				
			||||||
      inverted: true
 | 
					      inverted: true
 | 
				
			||||||
 | 
					  - platform: gpio
 | 
				
			||||||
 | 
					    name: PCA6416A binary sensor
 | 
				
			||||||
 | 
					    pin:
 | 
				
			||||||
 | 
					      pca6416a: pca6416a_hub
 | 
				
			||||||
 | 
					      number: 15
 | 
				
			||||||
 | 
					      mode: INPUT
 | 
				
			||||||
 | 
					      inverted: true
 | 
				
			||||||
  - platform: gpio
 | 
					  - platform: gpio
 | 
				
			||||||
    name: MCP21 binary sensor
 | 
					    name: MCP21 binary sensor
 | 
				
			||||||
    pin:
 | 
					    pin:
 | 
				
			||||||
@@ -2934,6 +2964,11 @@ pca9554:
 | 
				
			|||||||
    address: 0x3F
 | 
					    address: 0x3F
 | 
				
			||||||
    i2c_id: i2c_bus
 | 
					    i2c_id: i2c_bus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pca6416a:
 | 
				
			||||||
 | 
					  - id: pca6416a_hub
 | 
				
			||||||
 | 
					    address: 0x21
 | 
				
			||||||
 | 
					    i2c_id: i2c_bus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mcp23017:
 | 
					mcp23017:
 | 
				
			||||||
  - id: mcp23017_hub
 | 
					  - id: mcp23017_hub
 | 
				
			||||||
    open_drain_interrupt: true
 | 
					    open_drain_interrupt: true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -374,6 +374,16 @@ binary_sensor:
 | 
				
			|||||||
    on_press:
 | 
					    on_press:
 | 
				
			||||||
      - logger.log: Touched
 | 
					      - logger.log: Touched
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: gpio
 | 
				
			||||||
 | 
					    name: MaxIn Pin 4
 | 
				
			||||||
 | 
					    pin:
 | 
				
			||||||
 | 
					      max6956: max6956_1
 | 
				
			||||||
 | 
					      number: 4
 | 
				
			||||||
 | 
					      mode:
 | 
				
			||||||
 | 
					        input: true
 | 
				
			||||||
 | 
					        pullup: true
 | 
				
			||||||
 | 
					      inverted: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
climate:
 | 
					climate:
 | 
				
			||||||
  - platform: tuya
 | 
					  - platform: tuya
 | 
				
			||||||
    id: tuya_climate
 | 
					    id: tuya_climate
 | 
				
			||||||
@@ -717,4 +727,8 @@ voice_assistant:
 | 
				
			|||||||
        format: "Voice assistant error - code %s, message: %s"
 | 
					        format: "Voice assistant error - code %s, message: %s"
 | 
				
			||||||
        args: [code.c_str(), message.c_str()]
 | 
					        args: [code.c_str(), message.c_str()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					max6956:
 | 
				
			||||||
 | 
					  - id: max6956_1
 | 
				
			||||||
 | 
					    address: 0x40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
es8388:
 | 
					es8388:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user