feat: add external component and some test configurations
This commit is contained in:
parent
4c612fc462
commit
5b0930d616
11 changed files with 511 additions and 6 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Gitignore settings for ESPHome
|
||||
# This is an example and may include too much for your use-case.
|
||||
# You can modify this file to suit your needs.
|
||||
/.esphome/
|
||||
/secrets.yaml
|
||||
/secrets.*.yaml
|
||||
!/secrets.example.yaml
|
||||
53
components/wordcl/__init__.py
Normal file
53
components/wordcl/__init__.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import light
|
||||
from esphome.components import uart
|
||||
from esphome.components import sensor
|
||||
from esphome.const import CONF_ID, CONF_HEIGHT, CONF_TIMEOUT, ICON_GAUGE
|
||||
|
||||
DEPENDENCIES = ['time']
|
||||
AUTO_LOAD = ['light']
|
||||
|
||||
wordclock_ns = cg.esphome_ns.namespace('wordcl')
|
||||
|
||||
Wordclock = wordclock_ns.class_('Wordclock', cg.Component, light.)
|
||||
Desky = desky_ns.class_('Desky', cg.Component, uart.UARTDevice)
|
||||
|
||||
CONF_UP = "up"
|
||||
CONF_DOWN = "down"
|
||||
CONF_REQUEST = "request"
|
||||
CONF_STOPPING_DISTANCE = "stopping_distance"
|
||||
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(Desky),
|
||||
cv.Optional(CONF_UP): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_DOWN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_REQUEST): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_HEIGHT): sensor.sensor_schema(icon=ICON_GAUGE, accuracy_decimals=0),
|
||||
cv.Optional(CONF_STOPPING_DISTANCE, default=15): cv.positive_int,
|
||||
cv.Optional(CONF_TIMEOUT): cv.time_period,
|
||||
}).extend(uart.UART_DEVICE_SCHEMA)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
if CONF_UP in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_UP])
|
||||
cg.add(var.set_up_pin(pin))
|
||||
if CONF_DOWN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_DOWN])
|
||||
cg.add(var.set_down_pin(pin))
|
||||
if CONF_REQUEST in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_REQUEST])
|
||||
cg.add(var.set_request_pin(pin))
|
||||
if CONF_HEIGHT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HEIGHT])
|
||||
cg.add(var.set_height_sensor(sens))
|
||||
cg.add(var.set_stopping_distance(config[CONF_STOPPING_DISTANCE]))
|
||||
if CONF_TIMEOUT in config:
|
||||
cg.add(var.set_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
||||
|
||||
116
components/wordcl/desky.cpp
Normal file
116
components/wordcl/desky.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#include "desky.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace desky {
|
||||
|
||||
static const char *TAG = "desky";
|
||||
|
||||
const char *desky_operation_to_str(DeskyOperation op) {
|
||||
switch (op) {
|
||||
case DESKY_OPERATION_IDLE:
|
||||
return "IDLE";
|
||||
case DESKY_OPERATION_RAISING:
|
||||
return "RAISING";
|
||||
case DESKY_OPERATION_LOWERING:
|
||||
return "LOWERING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void Desky::setup() {
|
||||
if (this->up_pin_ != nullptr)
|
||||
this->up_pin_->digital_write(false);
|
||||
if (this->down_pin_ != nullptr)
|
||||
this->down_pin_->digital_write(false);
|
||||
if (this->request_pin_ != nullptr) {
|
||||
this->request_pin_->digital_write(true);
|
||||
this->request_time_ = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void Desky::loop() {
|
||||
static int state = 0;
|
||||
static uint8_t high_byte;
|
||||
|
||||
while (this->available()) {
|
||||
uint8_t c;
|
||||
int value;
|
||||
this->read_byte(&c);
|
||||
switch (state) {
|
||||
case 0:
|
||||
if (c == 1)
|
||||
state = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (c == 1)
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
break;
|
||||
case 2:
|
||||
high_byte = c;
|
||||
state = 3;
|
||||
break;
|
||||
case 3:
|
||||
value = (high_byte << 8) + c;
|
||||
this->current_pos_ = value;
|
||||
if (this->height_sensor_ != nullptr)
|
||||
this->height_sensor_->publish_state(value);
|
||||
state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->target_pos_ >= 0) {
|
||||
if (abs(this->target_pos_ - this->current_pos_) < this->stopping_distance_)
|
||||
this->stop();
|
||||
if ((this->timeout_ >= 0) && (millis() - this->start_time_ >= this->timeout_))
|
||||
this->stop();
|
||||
}
|
||||
|
||||
if ((this->request_time_ > 0) && (millis() - this->request_time_ >= 100)) {
|
||||
this->request_pin_->digital_write(false);
|
||||
this->request_time_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Desky::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Desky desk:");
|
||||
LOG_SENSOR("", "Height", this->height_sensor_);
|
||||
LOG_PIN("Up pin: ", this->up_pin_);
|
||||
LOG_PIN("Down pin: ", this->down_pin_);
|
||||
LOG_PIN("Request pin: ", this->request_pin_);
|
||||
}
|
||||
|
||||
void Desky::move_to(int target_pos) {
|
||||
if (abs(target_pos - this->current_pos_) < this->stopping_distance_)
|
||||
return;
|
||||
if (target_pos > this->current_pos_) {
|
||||
if (this->up_pin_ == nullptr)
|
||||
return;
|
||||
this->up_pin_->digital_write(true);
|
||||
this->current_operation = DESKY_OPERATION_RAISING;
|
||||
} else {
|
||||
if (this->down_pin_ == nullptr)
|
||||
return;
|
||||
this->down_pin_->digital_write(true);
|
||||
this->current_operation = DESKY_OPERATION_LOWERING;
|
||||
}
|
||||
this->target_pos_ = target_pos;
|
||||
if (this->timeout_ >= 0)
|
||||
this->start_time_ = millis();
|
||||
}
|
||||
|
||||
void Desky::stop() {
|
||||
this->target_pos_ = -1;
|
||||
if (this->up_pin_ != nullptr)
|
||||
this->up_pin_->digital_write(false);
|
||||
if (this->down_pin_ != nullptr)
|
||||
this->down_pin_->digital_write(false);
|
||||
this->current_operation = DESKY_OPERATION_IDLE;
|
||||
}
|
||||
|
||||
} // namespace desky
|
||||
} // namespace esphome
|
||||
52
components/wordcl/desky.h
Normal file
52
components/wordcl/desky.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace desky {
|
||||
|
||||
enum DeskyOperation : uint8_t {
|
||||
DESKY_OPERATION_IDLE = 0,
|
||||
DESKY_OPERATION_RAISING,
|
||||
DESKY_OPERATION_LOWERING,
|
||||
};
|
||||
|
||||
const char *desky_operation_to_str(DeskyOperation op);
|
||||
|
||||
class Desky : public Component, public sensor::Sensor, public uart::UARTDevice {
|
||||
public:
|
||||
float get_setup_priority() const override { return setup_priority::LATE; }
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_height_sensor(sensor::Sensor *sensor) { this->height_sensor_ = sensor; }
|
||||
void set_up_pin(GPIOPin *pin) { this->up_pin_ = pin; }
|
||||
void set_down_pin(GPIOPin *pin) { this->down_pin_ = pin; }
|
||||
void set_request_pin(GPIOPin *pin) { this->request_pin_ = pin; }
|
||||
void set_stopping_distance(int distance) { this->stopping_distance_ = distance; }
|
||||
void set_timeout(int timeout) { this->timeout_ = timeout; }
|
||||
|
||||
void move_to(int height);
|
||||
void stop();
|
||||
|
||||
DeskyOperation current_operation{DESKY_OPERATION_IDLE};
|
||||
|
||||
protected:
|
||||
sensor::Sensor *height_sensor_{nullptr};
|
||||
GPIOPin *up_pin_{nullptr};
|
||||
GPIOPin *down_pin_{nullptr};
|
||||
GPIOPin *request_pin_{nullptr};
|
||||
int stopping_distance_;
|
||||
int current_pos_{0};
|
||||
int target_pos_{-1};
|
||||
int timeout_{-1};
|
||||
uint64_t start_time_;
|
||||
uint64_t request_time_{0};
|
||||
};
|
||||
|
||||
} // namespace desky
|
||||
} // namespace esphome
|
||||
66
display_test.yaml
Normal file
66
display_test.yaml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
esphome:
|
||||
name: "display_thing"
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
framework:
|
||||
version: recommended
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
ap:
|
||||
ssid: "${devicename}"
|
||||
password: !secret ap_password
|
||||
manual_ip:
|
||||
static_ip: !secret manualip_static_ip
|
||||
gateway: !secret manualip_gateway
|
||||
subnet: !secret manualip_subnet
|
||||
dns1: 1.1.1.1
|
||||
dns2: 1.0.0.1
|
||||
|
||||
api:
|
||||
|
||||
ota:
|
||||
password: "${devicename}"
|
||||
|
||||
logger:
|
||||
# esp8266_store_log_strings_in_flash: false
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
light:
|
||||
- name: NeoPixel Strip 1
|
||||
id: neopixel_strip_1
|
||||
platform: neopixelbus
|
||||
type: GRB
|
||||
variant: WS2812
|
||||
pin: GPIO3
|
||||
num_leds: 200
|
||||
# default_transition_length: 0.2s
|
||||
method:
|
||||
type: esp8266_dma
|
||||
|
||||
display:
|
||||
- platform: addressable_light
|
||||
id: led_matrix_display
|
||||
addressable_light_id: neopixel_strip_1
|
||||
width: 18
|
||||
height: 11
|
||||
rotation: 0°
|
||||
update_interval: 16ms
|
||||
lambda: |-
|
||||
// Draw a bulls-eye pattern
|
||||
Color red = Color(0xFF0000);
|
||||
Color green = Color(0x00FF00);
|
||||
Color blue = Color(0x0000FF);
|
||||
it.rectangle(0, 0, 18, 11, red);
|
||||
it.rectangle(1, 1, 17, 10, green);
|
||||
it.rectangle(2, 2, 16, 9, blue);
|
||||
it.rectangle(3, 3, 15, 8, red);
|
||||
|
||||
|
||||
time:
|
||||
- platform: sntp
|
||||
id: current_time
|
||||
timezone: !secret timezone
|
||||
67
flake.nix
Normal file
67
flake.nix
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
description = "EspHome Word Clock";
|
||||
inputs = {
|
||||
systems.url = "github:nix-systems/x86_64-linux";
|
||||
# nixpkgs.url = "github:NixOS/nixpkgs/unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/22.11";
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
utils.inputs.systems.follows = "systems";
|
||||
};
|
||||
outputs = { self, nixpkgs, utils, ... }: utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let pkgs = import nixpkgs { inherit system; };
|
||||
in {
|
||||
|
||||
# This block here is used when running `nix develop`
|
||||
devShells.default = pkgs.mkShell rec {
|
||||
# Update the name to something that suites your project.
|
||||
name = "esphome-wordclock";
|
||||
|
||||
# build environment dependencies
|
||||
packages = with pkgs; [
|
||||
esphome
|
||||
];
|
||||
|
||||
# Setting up the environment variables you need during development.
|
||||
|
||||
# Todo figure out why I can't use clang on Asahi but can on Darwin
|
||||
# Use "clang++" for most systems but OSX Asahi requires g++ for some reason or a runtime error occurs
|
||||
shellHook = let
|
||||
# This is for an icon that is used below for the command line input below
|
||||
icon = "f121";
|
||||
in ''
|
||||
export PS1="$(echo -e '\u${icon}') {\[$(tput sgr0)\]\[\033[38;5;228m\]\w\[$(tput sgr0)\]\[\033[38;5;15m\]} (${name}) \\$ \[$(tput sgr0)\]"
|
||||
# export COMPILER="clang++"
|
||||
#export COMPILER="g++"
|
||||
echo "test";
|
||||
'';
|
||||
};
|
||||
# This is used when running `nix build`
|
||||
# packages.default = pkgs.llvmPackages_14.stdenv.mkDerivation rec {
|
||||
packages.default = pkgs.esphome.stdenv.mkDerivation rec {
|
||||
name = "esphome-wordclock";
|
||||
version = "2.0.0";
|
||||
src = self;
|
||||
|
||||
# buildInputs = [ pkgs.esphome ];
|
||||
|
||||
# buildPhase = "COMPILER='clang++' make";
|
||||
|
||||
# installPhase = ''
|
||||
# mkdir -p $out/bin;
|
||||
# install -t $out/bin worm
|
||||
# '';
|
||||
|
||||
# meta = with inputs.utils.lib; {
|
||||
# homepage = "https://github.com/icecreammatt/ssu-cs315-worm";
|
||||
# description = ''
|
||||
# Terminal CLI Worm Game
|
||||
# '';
|
||||
# };
|
||||
};
|
||||
# packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
|
||||
|
||||
# packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
|
||||
|
||||
});
|
||||
}
|
||||
7
secrets.example.yaml
Normal file
7
secrets.example.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
wifi_password: secret
|
||||
wifi_ssid: example
|
||||
manualip_static_ip: 192.168.1.177
|
||||
manualip_gateway: 192.168.1.1
|
||||
manualip_subnet: 255.255.255.0
|
||||
timezone: Europe/Zurich
|
||||
ap_password: "password for internal fallback accesspoint"
|
||||
12
wordclock.h
12
wordclock.h
|
|
@ -72,6 +72,7 @@ class Wordclock : public Component, public CustomAPIDevice {
|
|||
CRGB leds[WORDCLOCK_NUM_LEDS];
|
||||
int hour = -1;
|
||||
int minute = -1;
|
||||
int second = -1;
|
||||
int red = 124;
|
||||
int green = 124;
|
||||
int blue = 124;
|
||||
|
|
@ -83,7 +84,7 @@ class Wordclock : public Component, public CustomAPIDevice {
|
|||
clear_all_leds();
|
||||
FastLED.show();
|
||||
// TODO: Set up some kind of initialization sequence. But it should be based on an effect or similarly supporting the
|
||||
// cooperative multithreading. delay() calls are uncalled for. ^^
|
||||
// cooperative multithreading. delay() calls are uncalled for.
|
||||
}
|
||||
|
||||
void clear_all_leds() {
|
||||
|
|
@ -195,16 +196,21 @@ class Wordclock : public Component, public CustomAPIDevice {
|
|||
if (fastledlight2.get_state() > 0 ) {
|
||||
brightness = (int) (fastledlight2.get_brightness()*125);
|
||||
} else {
|
||||
ESP_LOGD("loop", "fastledlight state off - b: %i rgb %i %i %i", brightness, red, green, blue); delay(100);
|
||||
// ESP_LOGD("loop", "fastledlight state off - b: %i rgb %i %i %i", brightness, red, green, blue); delay(100);
|
||||
}
|
||||
|
||||
FastLED.setBrightness(brightness);
|
||||
FastLED.show();
|
||||
//check if valid time. Blink red,green,blue until valid time is present
|
||||
if (!time.is_valid() == false) {
|
||||
if (time.is_valid() == false) {
|
||||
ESP_LOGD("loop", "time is not valid");
|
||||
// do something to show that the clock isn't dead. Maybe I can instantiate and effect and use that for this.
|
||||
}
|
||||
else {
|
||||
if (time.second != second) {
|
||||
second = time.second;
|
||||
ESP_LOGD("loop", "time is now [%02i:%02i:%02i]", time.hour, time.minute, time.second);
|
||||
}
|
||||
if(time.hour != hour || time.minute != minute) {
|
||||
hour = time.hour;
|
||||
minute = time.minute;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,21 @@ esphome:
|
|||
includes:
|
||||
- wordclock.h
|
||||
|
||||
esp32:
|
||||
board: wemos_d1_mini32
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: components
|
||||
components: [ wordcl ]
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
framework:
|
||||
type: arduino
|
||||
version: recommended
|
||||
|
||||
# esp32:
|
||||
# board: ttgo-t7-v13-mini32
|
||||
# framework:
|
||||
# type: arduino
|
||||
|
||||
substitutions:
|
||||
devicename: wordclock
|
||||
|
|
@ -27,6 +38,8 @@ wifi:
|
|||
static_ip: !secret manualip_static_ip
|
||||
gateway: !secret manualip_gateway
|
||||
subnet: !secret manualip_subnet
|
||||
dns1: 1.1.1.1
|
||||
dns2: 1.0.0.1
|
||||
|
||||
api:
|
||||
|
||||
|
|
|
|||
89
wordclock8266.yaml
Normal file
89
wordclock8266.yaml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
esphome:
|
||||
name: "wordclock"
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
framework:
|
||||
version: recommended
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: components
|
||||
components: [ wordcl ]
|
||||
|
||||
# substitutions:
|
||||
# devicename: wordclock
|
||||
# friendly_name: "Wordclock"
|
||||
# light_friendly_name: "Wordclock Light"
|
||||
|
||||
# wifi:
|
||||
# ssid: !secret wifi_ssid
|
||||
# password: !secret wifi_password
|
||||
# ap:
|
||||
# ssid: "${devicename}"
|
||||
# password: !secret ap_password
|
||||
# manual_ip:
|
||||
# static_ip: !secret manualip_static_ip
|
||||
# gateway: !secret manualip_gateway
|
||||
# subnet: !secret manualip_subnet
|
||||
# dns1: 1.1.1.1
|
||||
# dns2: 1.0.0.1
|
||||
|
||||
# api:
|
||||
|
||||
# ota:
|
||||
# password: "${devicename}"
|
||||
|
||||
# logger:
|
||||
# # esp8266_store_log_strings_in_flash: false
|
||||
# web_server:
|
||||
# port: 80
|
||||
|
||||
wordclock:
|
||||
|
||||
light:
|
||||
- name: neopixel
|
||||
id: neopixel_1
|
||||
platform: neopixelbus
|
||||
type: GRB
|
||||
variant: WS2812
|
||||
pin: GPIO3
|
||||
num_leds: 30
|
||||
default_transition_length: 0.2s
|
||||
# restore_mode: RESTORE_DEFAULT_ON
|
||||
method:
|
||||
type: esp8266_dma
|
||||
on_turn_on:
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: neopixel_1
|
||||
brightness: 35%
|
||||
effect: rainbow
|
||||
effects:
|
||||
# - random:
|
||||
# - pulse:
|
||||
# - strobe:
|
||||
# - flicker:
|
||||
- addressable_rainbow:
|
||||
name: rainbow
|
||||
# - addressable_color_wipe:
|
||||
# - addressable_scan:
|
||||
# - addressable_twinkle:
|
||||
# - addressable_random_twinkle:
|
||||
# - addressable_fireworks:
|
||||
# - addressable_flicker:
|
||||
# - wled:
|
||||
|
||||
|
||||
|
||||
|
||||
# time:
|
||||
# - platform: sntp
|
||||
# id: current_time
|
||||
# timezone: !secret timezone
|
||||
|
||||
# - light.turn_on:
|
||||
# id: light_1
|
||||
# brightness: 100%
|
||||
# effect: addressable_rainbow
|
||||
29
wordclock_new.yaml
Normal file
29
wordclock_new.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
esphome:
|
||||
name: wordclock-livingroom
|
||||
|
||||
esp32:
|
||||
board: ttgo-t7-v13-mini32
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
password: ""
|
||||
|
||||
ota:
|
||||
password: ""
|
||||
|
||||
wifi:
|
||||
ssid: "wifithing"
|
||||
password: "lostandfound1"
|
||||
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "Wordclock-Livingroom"
|
||||
password: "wGfGBPnJcbzE"
|
||||
|
||||
captive_portal:
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue