# todos: # - move code into packages to make this re-useable # - testing substitutions: ota_password: "" api_enckey: "" wifi_ssid: "" wifi_password: "" vault_role_id: "" vault_secret_id: "" ###### nothing to change below this line ###### esphome: name: mlock-${name_of_board} includes: - mlock-common.h on_boot: - priority: 300 then: - light.addressable_set: { id: status_led, red: 0%, green: 0%, blue: 100% } - priority: -100 then: - if: condition: switch.is_on: mlock_${name_of_board}_switch then: - light.addressable_set: { id: status_led, red: 0%, green: 100%, blue: 0% } else: - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } esp8266: board: d1_mini restore_from_flash: true # max 96 bytes # Enable logging logger: api: encryption: key: $api_enckey ota: password: $ota_password wifi: ssid: $wifi_ssid password: $wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "$name_of_board Fallback Hotspot" password: "PZe2PJENtBiu" manual_ip: static_ip: $ip_addr gateway: 172.23.23.1 dns1: 172.23.23.1 subnet: 255.255.255.0 captive_portal: spi: clk_pin: GPIO14 mosi_pin: GPIO13 miso_pin: GPIO12 http_request: useragent: esphome timeout: 2s id: http_request_data globals: - id: vault_api_token type: std::string restore_value: no - id: rfid_tag type: std::string restore_value: no - id: access_allowed type: bool restore_value: no initial_value: 'false' - id: access_cache type: unsigned int[24] restore_value: yes initial_value: '{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}' script: - id: check_access then: - if: condition: lambda: |- int rfid_value = decode_token(id(rfid_tag)); for (int i = 0; i < 24; i++) { if (id(access_cache)[i] == rfid_value) { return true; } } return false; then: - script.execute: toggle_switch - script.wait: toggle_switch - if: # return LED to switch state before condition: switch.is_on: mlock_${name_of_board}_switch then: - light.addressable_set: { id: status_led, red: 0%, green: 100%, blue: 0% } else: - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } else: - script.execute: fetch_token_vault - id: fetch_token_vault then: # login to vault with role_id to fetch short lived token - http_request.post: url: https://vault.ctdo.de/v1/auth/approle/login verify_ssl: false headers: Content-Type: application/json json: role_id: $vault_role_id secret_id: $vault_secret_id on_response: # fetch token from response, store into vault_api_token then: - lambda: |- json::parse_json(id(http_request_data).get_string(), [](JsonObject root) { id(vault_api_token) = (const char*) root["auth"]["client_token"]; }); - script.execute: check_access_vault - id: check_access_vault then: - lambda: |- id(access_allowed) = false; - http_request.get: url: !lambda |- return ((std::string) "https://vault.ctdo.de/v1/maschinenlock/" + id(rfid_tag)); verify_ssl: false headers: X-Vault-Token: !lambda return id(vault_api_token).c_str(); on_response: then: - if: condition: lambda: 'return status_code == 200;' then: # when found, check if machine is allowed, turn on output or blink LED red - lambda: |- json::parse_json(id(http_request_data).get_string(), [](JsonObject root) { id(access_allowed) = (root["data"]["mlock-$name_of_board"] == "1"); }); - if: condition: lambda: 'return id(access_allowed);' then: - script.execute: toggle_switch - script.execute: cache_token - script.wait: toggle_switch else: - repeat: count: 3 then: - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } - delay: 0.1s - light.addressable_set: { id: status_led, red: 0%, green: 0%, blue: 0% } - delay: 0.1s else: # vault returns 404 on missing/unknown Tag so blink LED - repeat: count: 3 then: - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } - delay: 0.5s - light.addressable_set: { id: status_led, red: 0%, green: 0%, blue: 0% } - delay: 0.5s - if: # return LED to switch state before condition: switch.is_on: mlock_${name_of_board}_switch then: - light.addressable_set: { id: status_led, red: 0%, green: 100%, blue: 0% } else: - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } - id: toggle_switch then: - if: condition: switch.is_on: mlock_${name_of_board}_switch then: - switch.turn_off: mlock_${name_of_board}_switch - homeassistant.event: event: esphome.mlock_locked data: tag: !lambda return id(rfid_tag); machine: ${name_of_board} else: - switch.turn_on: mlock_${name_of_board}_switch - homeassistant.event: event: esphome.mlock_unlocked data: tag: !lambda return id(rfid_tag); machine: ${name_of_board} - text.set: id: ${name_of_board}_letzte_entsperrung value: !lambda return id(rfid_tag); - id: cache_token then: - lambda: |- int rfid_value = decode_token(id(rfid_tag)); int rfid_value_pos = 23; // search the token in the list to keep the access_cache unique for (int i = 0; i < 24; i++) { if (id(access_cache)[i] == rfid_value) { rfid_value_pos = i; break; } } // shift the existing entries down for (int i = rfid_value_pos; i >= 1; i--) { id(access_cache)[i] = id(access_cache)[i - 1]; } // write the new token to the start id(access_cache)[0] = rfid_value; rc522_spi: cs_pin: GPIO15 on_tag: then: - light.addressable_set: { id: status_led, red: 100%, green: 60%, blue: 0% } # small delay so the light can update its color - delay: 15ms # store the tag id into global variable - lambda: |- id(rfid_tag) = x; - script.execute: check_access - text.set: id: ${name_of_board}_letzter_token value: !lambda return id(rfid_tag); # switch component for the output state switch: - platform: gpio pin: D1 name: "Relais Output" id: mlock_${name_of_board}_switch internal: true # hide from Homeassistant, so no one can turn it on without Tag-Scanning binary_sensor: # sensor input for Turning Device off - platform: gpio pin: number: D3 inverted: true mode: INPUT_PULLUP id: ${name_of_board}gpio_input_ausschalter on_press: - switch.turn_off: mlock_${name_of_board}_switch - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } # a template sensor for showing the current switch state (read only) - platform: template id: mlock_${name_of_board}_state_output name: "${name_of_board} Status Ausgang" publish_initial_state: true lambda: |- return id(mlock_${name_of_board}_switch).state; # a button element for Homeassistant UI to allow turning off button: - platform: template name: "${name_of_board} Ausschalter" id: ${name_of_board}_btn_ausschalter on_press: - switch.turn_off: mlock_${name_of_board}_switch - light.addressable_set: { id: status_led, red: 100%, green: 0%, blue: 0% } - platform: template name: "${name_of_board} Tokencache leeren" id: ${name_of_board}_btn_tokencache_leeren on_press: - lambda: |- for (int i = 0; i < 24; i++) { id(access_cache)[i] = 0; } light: - platform: neopixelbus type: GRB variant: WS2812 pin: D4 name: status_led id: status_led num_leds: 1 internal: true text: - platform: template name: "$name_of_board Letzte Entsperung" id: ${name_of_board}_letzte_entsperrung optimistic: true mode: text - platform: template name: "$name_of_board Zuletzt Gelesener Token" id: ${name_of_board}_letzter_token optimistic: true mode: text