weewx-proxy/lib/weewx_proxy/modbus/deye.ex
Daniel Kempkens c5e58816db
All checks were successful
Build / build (push) Successful in 3m10s
feat(deye): ignore invalid counter increments
2024-08-09 20:14:49 +02:00

151 lines
3.7 KiB
Elixir

defmodule WeewxProxy.Modbus.Deye do
@moduledoc false
require Logger
use Tortoise311.Handler
alias WeewxProxy.{Publisher, Utils}
# Callbacks
@impl true
def init(_opts) do
_ = Logger.info("Initializing handler")
{:ok, 0}
end
@impl true
def connection(:up, state) do
_ = Logger.info("Connection has been established")
{:ok, state}
end
@impl true
def connection(:down, state) do
_ = Logger.warning("Connection has been dropped")
{:ok, state}
end
@impl true
def connection(:terminating, state) do
_ = Logger.warning("Connection is terminating")
{:ok, state}
end
@impl true
def subscription(:up, topic, state) do
_ = Logger.info("Subscribed to `#{topic}'")
{:ok, state}
end
@impl true
def subscription({:warn, [requested: req, accepted: qos]}, topic, state) do
_ = Logger.warning("Subscribed to `#{topic}'; requested #{req} but got accepted with QoS #{qos}")
{:ok, state}
end
@impl true
def subscription({:error, reason}, topic, state) do
_ = Logger.error("Error subscribing to `#{topic}'; #{inspect(reason)}")
{:ok, state}
end
@impl true
def subscription(:down, topic, state) do
_ = Logger.info("Unsubscribed from `#{topic}'")
{:ok, state}
end
@impl true
def handle_message(["bitshake", "tele", "smartmeter", topic], publish, state) do
current_time = Time.utc_now()
current_hour = current_time.hour
new_state = if current_hour == 0, do: 0, else: state
if topic == "SENSOR" do
{:ok, meter_data} = Jason.decode(publish)
timestamp = DateTime.utc_now() |> DateTime.to_unix()
day = get_in(meter_data, ["eBZ", "E_in"])
day_export = get_in(meter_data, ["eBZ", "E_out"])
active = get_in(meter_data, ["eBZ", "Power"])
mqtt_data = %{
dateTime: timestamp,
homeEnergyDay: day,
homeEnergyExportDay: day_export,
homeEnergyActive: active
}
:ok = Publisher.publish("weewx/ingest_si", mqtt_data)
:ok = Publisher.publish_value_map("hadata/bitShake", mqtt_data)
{:ok, new_state}
else
{:ok, new_state}
end
end
@impl true
def handle_message(topic, publish, state) do
full_topic = Enum.join(topic, "/")
parsed_message = parse_message(full_topic, publish)
state = handle_reading(full_topic, parsed_message, state)
{:ok, state}
end
@impl true
def terminate(reason, _state) do
_ = Logger.warning("Client has been terminated with reason: `#{inspect(reason)}'")
:ok
end
# Helper
@spec parse_message(String.t(), String.t()) :: float() | nil
defp parse_message(topic, message) when topic in ["deye/day_energy", "deye/ac/active_power"] do
Utils.parse_float(message)
end
defp parse_message(_topic, _message), do: nil
@spec handle_reading(String.t(), float() | nil, {:day | :active, float()}) :: {:day | :active, float()} | nil
defp handle_reading(_topic, nil, state), do: state
defp handle_reading("deye/day_energy", reading, old_reading) do
if reading >= old_reading do
timestamp = DateTime.utc_now() |> DateTime.to_unix()
data = %{
dateTime: timestamp,
solarEnergyDay: reading * 1000.0
}
:ok = Publisher.publish("weewx/ingest_si", data)
:ok = Publisher.publish_value_map("hadata/deye", data)
reading
else
_ = Logger.warning("Reading unexpectedly decreased from #{old_reading} to #{reading}")
old_reading
end
end
defp handle_reading("deye/ac/active_power", reading, state) do
timestamp = DateTime.utc_now() |> DateTime.to_unix()
data = %{
dateTime: timestamp,
solarEnergyActive: reading
}
:ok = Publisher.publish("weewx/ingest_si", data)
:ok = Publisher.publish_value_map("hadata/deye", data)
state
end
end