Added Raspiaudio Muse Luxe again as a sendspin player.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor, i2c, sensor
|
||||
from esphome.const import CONF_ID, ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||
|
||||
ns = cg.esphome_ns.namespace("luxe_ip5306")
|
||||
LuxeIP5306 = ns.class_("LuxeIP5306", cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LuxeIP5306),
|
||||
cv.Required("available"): binary_sensor.binary_sensor_schema(
|
||||
device_class="connectivity", entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Required("charging"): binary_sensor.binary_sensor_schema(
|
||||
device_class="battery_charging"
|
||||
),
|
||||
cv.Required("full"): binary_sensor.binary_sensor_schema(),
|
||||
cv.Required("charge_allowed"): binary_sensor.binary_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Required("level"): sensor.sensor_schema(
|
||||
unit_of_measurement="%", accuracy_decimals=0, device_class="battery",
|
||||
state_class="measurement", entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
cv.Required("current_limit"): sensor.sensor_schema(
|
||||
unit_of_measurement="mA", accuracy_decimals=0,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("10s"))
|
||||
.extend(i2c.i2c_device_schema(0x75))
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
for key in ("available", "charging", "full", "charge_allowed"):
|
||||
entity = await binary_sensor.new_binary_sensor(config[key])
|
||||
cg.add(getattr(var, f"set_{key}")(entity))
|
||||
for key in ("level", "current_limit"):
|
||||
entity = await sensor.new_sensor(config[key])
|
||||
cg.add(getattr(var, f"set_{key}")(entity))
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome::luxe_ip5306 {
|
||||
|
||||
// Optional IP5306-I2C on recent Luxe boards. Never changes power settings at boot.
|
||||
class LuxeIP5306 : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void set_available(binary_sensor::BinarySensor *s) { available_ = s; }
|
||||
void set_charging(binary_sensor::BinarySensor *s) { charging_ = s; }
|
||||
void set_full(binary_sensor::BinarySensor *s) { full_ = s; }
|
||||
void set_charge_allowed(binary_sensor::BinarySensor *s) { charge_allowed_ = s; }
|
||||
void set_level(sensor::Sensor *s) { level_ = s; }
|
||||
void set_current_limit(sensor::Sensor *s) { current_limit_ = s; }
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
void setup() override { update(); }
|
||||
|
||||
void update() override {
|
||||
uint8_t control, charge, full, limit, gauge;
|
||||
// Address-only ACK probe, then individual register reads. No assumption
|
||||
// that the chip supports register auto-increment.
|
||||
if (write(nullptr, 0) != i2c::ERROR_OK ||
|
||||
!read_byte(0x00, &control) || !read_byte(0x70, &charge) ||
|
||||
!read_byte(0x71, &full) || !read_byte(0x24, &limit)) {
|
||||
unavailable_();
|
||||
return;
|
||||
}
|
||||
valid_ = true;
|
||||
charge_enabled_ = (control & 0x10) != 0;
|
||||
available_->publish_state(true);
|
||||
charge_allowed_->publish_state(charge_enabled_);
|
||||
const bool full_flag = (full & 0x08) != 0;
|
||||
full_->publish_state(full_flag);
|
||||
charging_->publish_state(charge_enabled_ && (charge & 0x08) && !full_flag);
|
||||
// Configured current, NOT measured current. Keep it separate from the
|
||||
// charging state: 0x70 alone cannot establish USB presence in every mode.
|
||||
current_limit_->publish_state(50 + 100 * (limit & 0x1F));
|
||||
// Gauge is optional: undocumented on some revisions. Failure/unknown
|
||||
// codes must never be presented as an empty battery.
|
||||
float percent = NAN;
|
||||
if (read_byte(0x78, &gauge)) {
|
||||
switch (gauge >> 4) {
|
||||
case 0x0: percent = 100; break;
|
||||
case 0x8: percent = 75; break;
|
||||
case 0xC: percent = 50; break;
|
||||
case 0xE: percent = 25; break;
|
||||
case 0xF: percent = 0; break;
|
||||
}
|
||||
}
|
||||
level_->publish_state(percent);
|
||||
}
|
||||
|
||||
optional<bool> charge_enabled() const {
|
||||
if (!valid_) return {};
|
||||
return charge_enabled_;
|
||||
}
|
||||
|
||||
void set_charge_enabled(bool enabled) {
|
||||
uint8_t before;
|
||||
// Read afresh even if the last poll succeeded. Modify only bit 4, leaving
|
||||
// boost, key behaviour and all reserved bits untouched.
|
||||
if (write(nullptr, 0) != i2c::ERROR_OK || !read_byte(0x00, &before)) {
|
||||
unavailable_();
|
||||
ESP_LOGW("luxe_ip5306", "Recharge command rejected: circuit unavailable");
|
||||
return;
|
||||
}
|
||||
const uint8_t after = enabled ? (before | 0x10) : (before & ~0x10);
|
||||
if (after != before && !write_byte(0x00, after)) {
|
||||
unavailable_();
|
||||
ESP_LOGW("luxe_ip5306", "Recharge command failed");
|
||||
return;
|
||||
}
|
||||
update(); // Read-back is authoritative; the switch is not optimistic.
|
||||
if (valid_ && charge_enabled_ != enabled)
|
||||
ESP_LOGW("luxe_ip5306", "Recharge command not confirmed by circuit");
|
||||
}
|
||||
|
||||
protected:
|
||||
void unavailable_() {
|
||||
valid_ = false;
|
||||
available_->publish_state(false);
|
||||
charging_->invalidate_state();
|
||||
full_->invalidate_state();
|
||||
charge_allowed_->invalidate_state();
|
||||
level_->publish_state(NAN);
|
||||
current_limit_->publish_state(NAN);
|
||||
// No mark_failed(): an older board must still play audio, and a device
|
||||
// temporarily asleep or disconnected must be detected on the next poll.
|
||||
}
|
||||
|
||||
binary_sensor::BinarySensor *available_{nullptr};
|
||||
binary_sensor::BinarySensor *charging_{nullptr};
|
||||
binary_sensor::BinarySensor *full_{nullptr};
|
||||
binary_sensor::BinarySensor *charge_allowed_{nullptr};
|
||||
sensor::Sensor *level_{nullptr};
|
||||
sensor::Sensor *current_limit_{nullptr};
|
||||
bool valid_{false};
|
||||
bool charge_enabled_{false};
|
||||
};
|
||||
} // namespace esphome::luxe_ip5306
|
||||
Reference in New Issue
Block a user