/* Copyright (C) 2018-2022 Fredrik Öhrström (gpl-3.0-or-later) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Implemented January 2021 Xael South: Implements GSS, CC101 and CC301 energy meter. This T1 WM-Bus meter broadcasts: - accumulated energy consumption - accumulated energy consumption till yesterday - current date - actually measured voltage - actually measured current - actually measured frequency - meter status and errors The single-phase and three-phase send apparently the same datagram: three-phase meter sends voltage and current values for every phase L1 .. L3. Meter version. Implementation tested against meters: - CC101 one-phase with firmware version 0x01. - CC301 three-phase with firmware version 0x01. Encryption: None. */ #include"meters_common_implementation.h" namespace { struct Driver : public virtual MeterCommonImplementation { Driver(MeterInfo &mi, DriverInfo &di); }; static bool ok = registerDriver([](DriverInfo&di) { di.setName("gransystems"); di.setDefaultFields("name,id,total_energy_consumption_kwh,timestamp"); di.setMeterType(MeterType::ElectricityMeter); di.addLinkMode(LinkMode::T1); di.addDetection(MANUFACTURER_GSS, 0x02, 0x01); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); }); Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { addStringFieldWithExtractorAndLookup( "status", "Status of meter.", DEFAULT_PRINT_PROPERTIES | PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::ErrorFlags), { { { "ERROR_FLAGS", Translate::MapType::BitToString, AlwaysTrigger, AutoMask, "OK", { { 0x0001, "METER_HARDWARE_ERROR" }, { 0x0002, "RTC_ERROR" }, { 0x0100, "DSP_COMMUNICATION_ERROR" }, { 0x0200, "DSP_HARDWARE_ERROR" }, { 0x4000, "RAM_ERROR" }, { 0x8000, "ROM_ERROR" } } }, { "ERROR_FLAGS_SINGLE_PHASE", Translate::MapType::BitToString, TriggerBits(0x01020000), AutoMask, "OK", { { 0x0008, "DEVICE_NOT_CONFIGURED" }, { 0x0010, "INTERNAL_ERROR" }, { 0x0020, "BATTERY_LOW" }, { 0x0040, "MAGNETIC_FRAUD_PRESENT" }, { 0x0080, "MAGNETIC_FRAUD_PAST" }, } }, { "ERROR_FLAGS_THREE_PHASE", Translate::MapType::BitToString, TriggerBits(0x01010000), AutoMask, "OK", { { 0x0008, "CALIBRATION_EEPROM_ERROR" }, { 0x0010, "NETWORK_INTERFERENCE" }, { 0x0800, "CALIBRATION_EEPROM_ERROR" }, { 0x1000, "EEPROM1_ERROR" } } }, }, }); addStringFieldWithExtractorAndLookup( "info", "Is it a three phase or single phase meter.", DEFAULT_PRINT_PROPERTIES, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::ErrorFlags), { { { "INFO_FLAGS", Translate::MapType::IndexToString, AlwaysTrigger, AutoMask, "", { { 0x01020000, "SINGLE_PHASE_METER" }, { 0x01010000, "THREE_PHASE_METER" } } } }, }); addNumericFieldWithExtractor( "total_energy_consumption", "The total energy consumption recorded by this meter.", DEFAULT_PRINT_PROPERTIES, Quantity::Energy, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::AnyEnergyVIF) ); addNumericFieldWithExtractor( "total_energy_consumption_tariff_{tariff_counter}", "The total energy consumption recorded by this meter.", DEFAULT_PRINT_PROPERTIES, Quantity::Energy, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::AnyEnergyVIF) .set(TariffNr(1), TariffNr(4)) ); addNumericFieldWithExtractor( "target", "Last day?", DEFAULT_PRINT_PROPERTIES, Quantity::PointInTime, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::DateTime) .set(StorageNr(2)) ); addNumericFieldWithExtractor( "target_energy_consumption", "Last day energy consumption?", DEFAULT_PRINT_PROPERTIES, Quantity::Energy, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::AnyEnergyVIF) .set(StorageNr(2)) ); addNumericFieldWithExtractor( "target_energy_consumption_tariff_{tariff_counter}", "Last day energy consumption for tariff?", DEFAULT_PRINT_PROPERTIES, Quantity::Energy, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::AnyEnergyVIF) .set(TariffNr(1), TariffNr(4)) .set(StorageNr(2)) ); addNumericFieldWithExtractor( "device", "Device date time when telegram was sent.", DEFAULT_PRINT_PROPERTIES, Quantity::PointInTime, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::DateTime) ); addNumericFieldWithExtractor( "voltage_at_phase_1", "Voltage for single phase meter.", DEFAULT_PRINT_PROPERTIES, Quantity::Voltage, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Voltage) .set(SubUnitNr(0)) ); addNumericFieldWithExtractor( "voltage_at_phase_{subunit_counter}", "Voltage at phase L#.", DEFAULT_PRINT_PROPERTIES, Quantity::Voltage, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Voltage) .set(SubUnitNr(1), SubUnitNr(3)) ); addNumericFieldWithExtractor( "current_at_phase_1", "Amperage for single phase meter.", DEFAULT_PRINT_PROPERTIES, Quantity::Amperage, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Amperage) .set(SubUnitNr(0)) ); addNumericFieldWithExtractor( "current_at_phase_{subunit_counter}", "Amperage at phase L#.", DEFAULT_PRINT_PROPERTIES, Quantity::Amperage, VifScaling::Auto, DifSignedness::Signed, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Amperage) .set(SubUnitNr(1), SubUnitNr(3)) ); addNumericFieldWithExtractor( "raw_frequency", "Raw input to frequency.", DEFAULT_PRINT_PROPERTIES | PrintProperty::HIDE, Quantity::Frequency, VifScaling::None, DifSignedness::Signed, FieldMatcher::build() .set(DifVifKey("02FB2D")) ); addNumericFieldWithCalculator( "frequency", "Frequency of AC.", DEFAULT_PRINT_PROPERTIES, Quantity::Frequency, "raw_frequency_hz / 100 counter"); } } // Test: Gran101 gransystems 18046178 NOKEY // Comment: Gran-System-S electricity meter 101 // telegram=|7844731e78610418010278046d0f13bc21040394030000841003690300008420032b00000084300300000000848010030000000084016d0000bc2184010394030000841103690300008421032b00000084310300000000848110030000000004fd482e09000004fd5b0000000002fb2d861304fd1700000201| // {"media":"electricity","meter":"gransystems","name":"Gran101","id":"18046178","status":"OK","info":"SINGLE_PHASE_METER","total_energy_consumption_kwh":0.916,"total_energy_consumption_tariff_4_kwh":0,"total_energy_consumption_tariff_2_kwh":0.043,"total_energy_consumption_tariff_3_kwh":0,"total_energy_consumption_tariff_1_kwh":0.873,"target_datetime":"2021-01-28 00:00","target_energy_consumption_kwh":0.916,"target_energy_consumption_tariff_2_kwh":0.043,"target_energy_consumption_tariff_4_kwh":0,"target_energy_consumption_tariff_3_kwh":0,"target_energy_consumption_tariff_1_kwh":0.873,"device_datetime":"2021-01-28 19:15","voltage_at_phase_1_v":235,"current_at_phase_1_a":0,"frequency_hz":49.98,"timestamp":"1111-11-11T11:11:11Z"} // |Gran101;18046178;0.916;1111-11-11 11:11.11 // Test: Gran301 gransystems 20100117 NOKEY // Comment: Gran-System-S electricity meter 301 // telegram=|9e44731e17011020010278046d0813bc21040300000000841003000000008420030000000084300300000000848010030000000084016d0000bc218401030000000084110300000000842103000000008431030000000084811003000000008440fd4825090000848040fd480000000084c040fd48000000008440fd5b00000000848040fd5b0000000084c040fd5b0000000002fb2d881304fd1702000101| // {"media":"electricity","meter":"gransystems","name":"Gran301","id":"20100117","status":"RTC_ERROR","info":"THREE_PHASE_METER","total_energy_consumption_kwh":0,"total_energy_consumption_tariff_2_kwh":0,"total_energy_consumption_tariff_3_kwh":0,"total_energy_consumption_tariff_4_kwh":0,"total_energy_consumption_tariff_1_kwh":0,"target_datetime":"2021-01-28 00:00","target_energy_consumption_kwh":0,"target_energy_consumption_tariff_1_kwh":0,"target_energy_consumption_tariff_2_kwh":0,"target_energy_consumption_tariff_4_kwh":0,"target_energy_consumption_tariff_3_kwh":0,"device_datetime":"2021-01-28 19:08","voltage_at_phase_2_v":0,"voltage_at_phase_3_v":0,"voltage_at_phase_1_v":234.1,"current_at_phase_1_a":0,"current_at_phase_2_a":0,"current_at_phase_3_a":0,"frequency_hz":50,"timestamp":"1111-11-11T11:11:11Z"} // |Gran301;20100117;0;1111-11-11 11:11.11