#include "cbf_sthome.h" namespace solar { // see common.h for definition of publish_spec_t // publish_spec_t(int on_count, int interval, int timeout) const publish_spec_t cbf_sthome::publish_spec = publish_spec_t(3, 15, 30); // default publish spec const publish_spec_t cbf_sthome::rtr_publish_spec = publish_spec_t(2, 10, 40); // for remote transmission requests const std::string cbf_sthome::heartbeat = "HELLO\0\0\0"; // must be exactly 8 bytes including null terminators //cbf_sthome::cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr) // : cb_frame(msg_id, can_id, frame, rtr) // { // } cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src) { if (this != &src) { cb_frame::operator=(std::move(src)); src.clear(); //ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str()); } return *this; } void cbf_sthome::clear() { cb_frame::clear(); } bool cbf_sthome::verify_heartbeat() const { const auto& x = this->frame; std::string hb(reinterpret_cast(x.data()), x.size()); hb.erase(std::find_if(hb.rbegin(), hb.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), hb.end()); return hb == heartbeat; } // float cbf_sthome::_get_battery_charge_voltage_limit() const // { // const auto& x = this->frame; // return 0.1 * (float)((x[1] << 8) + x[0]); // } // Function to build a message from the pylon canbus frame std::string cbf_sthome::to_string() const { char buffer[128]; const auto& x = this->frame; switch (can_id) { case CB_CANBUS_ID01: return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : ""; case CB_CANBUS_ID02: return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : ""; case CB_CANBUS_ID03: return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : ""; case CB_CANBUS_ID04: return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : ""; case CB_CANBUS_ID05: return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : ""; case CB_CANBUS_ID06: return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : ""; case CB_CANBUS_ID07: return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : ""; case CB_CANBUS_ID08: return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : ""; case CB_CANBUS_ID09: return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : ""; case CB_CANBUS_ID10: return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : ""; case CB_GEYSER_TEMPERATURE_TOP: { if(rtr) { return "Request for geyser top temp"; } double temp_top = get_double(x[0], x[1], 256, true); snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top); return buffer; } case CB_GEYSER_TEMPERATURE_BOTTOM: { if(rtr) { return "Request for geyser bottom temp"; } double temp_bot = get_double(x[0], x[1], 256, true); snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot); return buffer; } case CB_GEYSER_TEMPERATURE_AMBIENT: { if(rtr) { return "Request for geyser ambient temp"; } double temp_amb = get_double(x[0], x[1], 256, true); snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb); return buffer; } case CB_GEYSER_HEATING: { if(rtr) { return "Request for geyser heating"; } double heating_loss = get_double(x[0], x[1], 64, true); // -512W to 512W double heat_gained = get_double(x[2], x[3], 128); // 0 to 512W double calculated_heat_loss = get_double(x[4], x[5], 128); // 0 to 512W unsigned int est_heating_time = (unsigned int) static_cast(256 * x[7] + x[6]); // 0 to 65536 seconds snprintf (buffer, sizeof(buffer), "Heat: loss %.3fW, gain %.3fW, calc loss %.3fW, est. heat time %ds", heating_loss, heat_gained, calculated_heat_loss, est_heating_time); return buffer; } case CB_GEYSER_ACTIVE_SCHEDULE: { if(rtr) { return "Request for geyser schedule"; } double active_schedule_temp = get_double(x[0], x[1], 256, true); // -128 to 128°C int active_heating_time = static_cast(256 * x[3] + x[2]); // -32768 to 32768s double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s int active_schedule_day = static_cast(x[6]); snprintf (buffer, sizeof(buffer), "Geyser Schedule: target %.2f°C, heating time %ds, overshoot %.1fs, day %d", active_schedule_temp, active_heating_time, heating_overshoot_time, active_schedule_day); return buffer; } case CB_POWER_MAINS: { if(rtr) { return "Request for power mains"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_INVERTER: { if(rtr) { return "Request for power inverter"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_PLUGS: { if(rtr) { return "Request for power plugs"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_LIGHTS: { if(rtr) { return "Request for power lights"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_GEYSER: { if(rtr) { return "Request for power geyser"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_POOL: { if(rtr) { return "Request for power pool"; } double power = get_double(x[0], x[1], 2048); // 0 to 32kW double voltage = get_double(x[2], x[3], 128); // 0 to 512V double current = get_double(x[4], x[5], 512); // 0 to 128A snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power); return buffer; } case CB_POWER_GENERATED: { if(rtr) { return "Request for power generated"; } double power_generated = get_double(x[0], x[1], 2048); // 0 to 32kW double power_loss = get_double(x[2], x[3], 2048); // 0 to 32kW snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss); return buffer; } case CB_ENERGY_MAINS: { if(rtr) { return "Request for energy mains"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Mains Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_GEYSER: { if(rtr) { return "Request for energy geyser"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Geyser Energy day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_POOL: { if(rtr) { return "Request for energy pool"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Pool Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_PLUGS: { if(rtr) { return "Request for energy plugs"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Plugs Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_LIGHTS: { if(rtr) { return "Request for energy lights"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Lights Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_HOUSE: { if(rtr) { return "Request for energy house"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "House Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_GENERATED: { if(rtr) { return "Request for energy generated"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Generated Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_ENERGY_LOSS: { if(rtr) { return "Request for energy loss"; } double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh snprintf (buffer, sizeof(buffer), "Energy Loss: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); return buffer; } case CB_CONTROLLER_STATES: { if(rtr) { return "Request for controller states"; } uint8_t alarms = x[0]; uint8_t states = x[1]; uint8_t modes = x[2]; bool inverter_1_battery_low = alarms & 0x80; bool inverter_2_battery_low = alarms & 0x10; bool inverter_overload = alarms & 0x08; bool geyser_heating = states & 0x80; bool geyser_energised = states & 0x40; bool mains_supply_present = states & 0x20; bool battery_charging = states & 0x08; bool vacation_mode = modes & 0x80; // nobody at home std::string mode = ""; int geysermode = (modes & 0x70) >> 4; switch (geysermode) { case GM_SUNDAY: mode = "SUN"; break; case GM_WORKDAY: mode = "WRK"; break; case GM_SATURDAY: mode = "SAT"; break; case GM_PUBLIC_HOLIDAY: mode = "PUB"; break; case GM_SCHOOL_HOLIDAY: mode = "SCH"; break; default: mode = "UNK"; break; } snprintf(buffer, sizeof(buffer), "STATES: %s%s%s%s MODE: %s%s INV: %s%s%s", geyser_heating ? "HEAT " : "", geyser_energised ? "GEYSER " : "", mains_supply_present ? "MAINS " : "", battery_charging ? "BATCHG " : "", vacation_mode ? "VACA ": "", mode.c_str(), inverter_1_battery_low ? "INV LOBAT " : "", inverter_2_battery_low ? "INV2 LOBAT " : "", inverter_overload ? "INV OVL " : ""); return buffer; } } if(rtr) { return "Request for unknown sthome CAN ID"; } return "Unknown CAN ID"; } } // namespace solar