From e6b743553b45b5b4348bbe74e4239afcd28c904a Mon Sep 17 00:00:00 2001
From: Guilhem CARRON <gcarron@grandlyon.com>
Date: Wed, 1 Dec 2021 15:35:55 +0000
Subject: [PATCH] feat(analysis): Add electricity consumption profile in
 analysis - new doctype enedis.maxpower - new doctype
 enedis.monthly.analysis.data - new cozy service that calculates the enedis
 monthly analysis - require Enedis-Konnector version 1.1.0 to get maxPower
 values

---
 manifest.webapp                               |   5 +
 scripts/createDayDataFiles.js                 |   2 +-
 src/assets/icons/ico/maxPower.svg             |   6 +
 src/assets/icons/ico/minimum.svg              |   5 +
 .../Analysis/ElecHalfHourChart.spec.tsx       |  68 ++++
 src/components/Analysis/ElecHalfHourChart.tsx | 120 +++++++
 .../ElecHalfHourMonthlyAnalysis.spec.tsx      |  80 +++++
 .../Analysis/ElecHalfHourMonthlyAnalysis.tsx  | 219 +++++++++++++
 src/components/Analysis/MonthlyAnalysis.tsx   |   8 +
 .../ElecHalfHourChart.spec.tsx.snap           |  44 +++
 .../ElecHalfHourMonthlyAnalysis.spec.tsx.snap |  20 ++
 .../Analysis/elecHalfHourMonthlyAnalysis.scss |  54 ++++
 src/components/Charts/AxisRight.tsx           |   4 +-
 src/components/Charts/Bar.tsx                 |  12 +-
 src/doctypes/com-grandlyon-enedis-maxpower.ts |   1 +
 ...-grandlyon-enedis-monthly-analysis-data.ts |   2 +
 src/doctypes/index.ts                         |  18 +-
 src/locales/fr.json                           |  12 +-
 src/models/enedisMonthlyAnalysis.ts           |  14 +
 src/models/maxPower.model.ts                  |   4 +
 src/services/consumption.service.ts           |  18 ++
 .../enedisMonthlyAnalysisData.service.spec.ts |  96 ++++++
 .../enedisMonthlyAnalysisData.service.ts      | 158 ++++++++++
 src/styles/components/_barchart.scss          |  14 +
 src/styles/index.css                          |  50 ++-
 .../services/enedisHalfHourMonthlyAnalysis.ts | 292 ++++++++++++++++++
 tests/__mocks__/datachartData.mock.ts         |  30 ++
 .../enedisMonthlyAnalysisData.mock.ts         | 106 +++++++
 28 files changed, 1435 insertions(+), 27 deletions(-)
 create mode 100644 src/assets/icons/ico/maxPower.svg
 create mode 100644 src/assets/icons/ico/minimum.svg
 create mode 100644 src/components/Analysis/ElecHalfHourChart.spec.tsx
 create mode 100644 src/components/Analysis/ElecHalfHourChart.tsx
 create mode 100644 src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx
 create mode 100644 src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx
 create mode 100644 src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap
 create mode 100644 src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap
 create mode 100644 src/components/Analysis/elecHalfHourMonthlyAnalysis.scss
 create mode 100644 src/doctypes/com-grandlyon-enedis-maxpower.ts
 create mode 100644 src/doctypes/com-grandlyon-enedis-monthly-analysis-data.ts
 create mode 100644 src/models/enedisMonthlyAnalysis.ts
 create mode 100644 src/models/maxPower.model.ts
 create mode 100644 src/services/enedisMonthlyAnalysisData.service.spec.ts
 create mode 100644 src/services/enedisMonthlyAnalysisData.service.ts
 create mode 100644 src/targets/services/enedisHalfHourMonthlyAnalysis.ts
 create mode 100644 tests/__mocks__/enedisMonthlyAnalysisData.mock.ts

diff --git a/manifest.webapp b/manifest.webapp
index 480d3f13a..a529266ad 100644
--- a/manifest.webapp
+++ b/manifest.webapp
@@ -130,6 +130,11 @@
     }
   },
   "services": {
+    "enedisHalfHourMonthlyAnalysis": {
+      "type": "node",
+      "file": "services/enedisHalfHourMonthlyAnalysis/ecolyo.js",
+      "trigger": "@cron 0 0 8 3 * *"
+    },
     "monthlyReportNotification": {
       "type": "node",
       "file": "services/monthlyReportNotification/ecolyo.js",
diff --git a/scripts/createDayDataFiles.js b/scripts/createDayDataFiles.js
index 40586040a..a6130c335 100644
--- a/scripts/createDayDataFiles.js
+++ b/scripts/createDayDataFiles.js
@@ -107,7 +107,7 @@ const generateHalfAnHourData = (_startingdate, _endingDate, min, max) => {
     hour: 0,
     minute: 0,
   })
-  
+
   monthDumpArray.push({
     load: Math.round(monthlyLoad * 100) / 100,
     year: lastYear,
diff --git a/src/assets/icons/ico/maxPower.svg b/src/assets/icons/ico/maxPower.svg
new file mode 100644
index 000000000..15c86fcec
--- /dev/null
+++ b/src/assets/icons/ico/maxPower.svg
@@ -0,0 +1,6 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="7" y="6" width="25" height="2" rx="1" fill="white"/>
+<path d="M17 14C17 12.8954 17.8954 12 19 12H20C21.1046 12 22 12.8954 22 14V33H17V14Z" fill="#D87B39"/>
+<path d="M9 24C9 22.8954 9.89543 22 11 22H12C13.1046 22 14 22.8954 14 24V33H9V24Z" fill="#D87B39"/>
+<path d="M25 28C25 26.8954 25.8954 26 27 26H28C29.1046 26 30 26.8954 30 28V33H25V28Z" fill="#D87B39"/>
+</svg>
diff --git a/src/assets/icons/ico/minimum.svg b/src/assets/icons/ico/minimum.svg
new file mode 100644
index 000000000..601fc8101
--- /dev/null
+++ b/src/assets/icons/ico/minimum.svg
@@ -0,0 +1,5 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M29.0636 23.2831C26.3793 27.021 21.6698 29.0536 16.8631 28.0942C10.3113 26.7865 6.05999 20.4151 7.36766 13.8632C8.03388 10.5253 10.0144 7.78445 12.6635 6.07163C7.31475 7.17394 2.8792 11.3674 1.74304 17.0599C0.228052 24.6505 5.15331 32.0321 12.7439 33.5471C20.086 35.0125 27.2325 30.4524 29.0636 23.2831Z" fill="#D87B39"/>
+<path d="M24 9.75311C24 9.9864 23.9379 10.1994 23.8138 10.3921L18.4996 18.3645H23.8782V20H16V19.1936C16 19.0922 16.0167 18.9959 16.0501 18.9046C16.0836 18.8082 16.1265 18.7195 16.1791 18.6383L21.5076 10.6279H16.3366V9H24V9.75311Z" fill="white"/>
+<path d="M35 5.75311C35 5.9864 34.9379 6.1994 34.8138 6.39212L29.4996 14.3645H34.8782V16H27V15.1936C27 15.0922 27.0167 14.9959 27.0501 14.9046C27.0836 14.8082 27.1265 14.7195 27.1791 14.6383L32.5076 6.62794H27.3366V5H35V5.75311Z" fill="white"/>
+</svg>
diff --git a/src/components/Analysis/ElecHalfHourChart.spec.tsx b/src/components/Analysis/ElecHalfHourChart.spec.tsx
new file mode 100644
index 000000000..702caef22
--- /dev/null
+++ b/src/components/Analysis/ElecHalfHourChart.spec.tsx
@@ -0,0 +1,68 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock'
+import { Provider } from 'react-redux'
+import configureStore from 'redux-mock-store'
+import ElecHalfHourChart from './ElecHalfHourChart'
+import * as reactRedux from 'react-redux'
+import { DateTime } from 'luxon'
+import { dataLoadArray } from '../../../tests/__mocks__/datachartData.mock'
+
+jest.mock('cozy-ui/transpiled/react/I18n', () => {
+  return {
+    useI18n: jest.fn(() => {
+      return {
+        t: (str: string) => str,
+      }
+    }),
+  }
+})
+const mockcompareStepDate = jest.fn()
+jest.mock('services/dateChart.service', () => {
+  return jest.fn(() => {
+    return {
+      compareStepDate: mockcompareStepDate,
+    }
+  })
+})
+const mockStore = configureStore([])
+const mockUseSelector = jest.spyOn(reactRedux, 'useSelector')
+
+describe('ElecHalfHourChart component', () => {
+  it('should be rendered correctly', () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+      },
+    })
+    mockUseSelector.mockReturnValue(
+      DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+        zone: 'utc',
+      })
+    )
+    const wrapper = mount(
+      <Provider store={store}>
+        <ElecHalfHourChart dataLoad={dataLoadArray} isWeekend={true} />
+      </Provider>
+    ).getElement()
+    expect(wrapper).toMatchSnapshot()
+  })
+  it('should render week data', () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+      },
+    })
+    mockUseSelector.mockReturnValue(
+      DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+        zone: 'utc',
+      })
+    )
+    const wrapper = mount(
+      <Provider store={store}>
+        <ElecHalfHourChart dataLoad={dataLoadArray} isWeekend={false} />
+      </Provider>
+    )
+    expect(wrapper.find('.week')).toBeTruthy()
+  })
+})
diff --git a/src/components/Analysis/ElecHalfHourChart.tsx b/src/components/Analysis/ElecHalfHourChart.tsx
new file mode 100644
index 000000000..3d2c1990f
--- /dev/null
+++ b/src/components/Analysis/ElecHalfHourChart.tsx
@@ -0,0 +1,120 @@
+import React, { useEffect, useRef, useState } from 'react'
+import Bar from 'components/Charts/Bar'
+import AxisBottom from 'components/Charts/AxisBottom'
+import AxisRight from 'components/Charts/AxisRight'
+import { FluidType } from 'enum/fluid.enum'
+import { scaleBand, ScaleBand, scaleLinear, ScaleLinear } from 'd3-scale'
+import { DateTime } from 'luxon'
+import { TimeStep } from 'enum/timeStep.enum'
+import { Dataload } from 'models'
+import './elecHalfHourMonthlyAnalysis.scss'
+
+interface ElecHalfHourChartProps {
+  dataLoad: Dataload[]
+  isWeekend: boolean
+}
+
+const ElecHalfHourChart = ({ dataLoad, isWeekend }: ElecHalfHourChartProps) => {
+  const [width, setWidth] = useState<number>(0)
+  const [height, setHeight] = useState<number>(0)
+  const chartContainer = useRef<HTMLDivElement>(null)
+  const marginLeft = 10
+  const marginRight = 10
+  const marginTop = 20
+  const marginBottom = 50
+  const getContentWidth = () => {
+    return width - marginLeft - marginRight
+  }
+
+  const getContentHeight = () => {
+    return height - marginTop - marginBottom
+  }
+  const getMaxLoad = () => {
+    const maxLoad = dataLoad
+      ? Math.max(...dataLoad.map((d: Dataload) => d.value))
+      : 0
+    return maxLoad
+  }
+
+  const xScale: ScaleBand<string> = scaleBand()
+    .domain(
+      dataLoad.map((d: Dataload) =>
+        d.date.toLocaleString(DateTime.DATETIME_SHORT)
+      )
+    )
+    .range([0, getContentWidth()])
+    .padding(0.2)
+
+  const yScale: ScaleLinear<number, number> = scaleLinear()
+    .domain([0, getMaxLoad()])
+    .range([getContentHeight(), 0])
+
+  useEffect(() => {
+    function handleResize() {
+      const maxWidth = 940
+      const maxHeight = 200
+      const _width = chartContainer.current
+        ? chartContainer.current.offsetWidth > maxWidth
+          ? maxWidth
+          : chartContainer.current.offsetWidth
+        : 400
+      setWidth(_width)
+      const _height = chartContainer.current
+        ? chartContainer.current.offsetHeight > maxHeight
+          ? maxHeight
+          : chartContainer.current.offsetHeight
+        : 200
+      setHeight(_height)
+    }
+    handleResize()
+    window.addEventListener('resize', handleResize)
+    return () => window.removeEventListener('resize', handleResize)
+  }, [])
+
+  return (
+    <div className="graph-elec-half-hour" ref={chartContainer}>
+      <svg width={width} height={height}>
+        <AxisRight
+          fluidType={FluidType.ELECTRICITY}
+          yScale={yScale}
+          width={width}
+          marginRight={marginRight}
+          marginTop={marginTop}
+          isAnalysis={true}
+        />
+        <g transform={`translate(${10},${10})`}>
+          {dataLoad.map((value, index) => {
+            return (
+              <Bar
+                key={index}
+                index={index}
+                dataload={value}
+                compareDataload={null}
+                fluidType={FluidType.ELECTRICITY}
+                timeStep={TimeStep.HALF_AN_HOUR}
+                showCompare={false}
+                xScale={xScale}
+                yScale={yScale}
+                height={getContentHeight()}
+                isSwitching={false}
+                isDuel={false}
+                weekdays={isWeekend ? 'weekend' : 'week'}
+              />
+            )
+          })}
+        </g>
+        <AxisBottom
+          data={dataLoad}
+          timeStep={TimeStep.HALF_AN_HOUR}
+          xScale={xScale}
+          height={height}
+          marginLeft={marginLeft}
+          marginBottom={marginBottom}
+          isDuel={false}
+        />
+      </svg>
+    </div>
+  )
+}
+
+export default ElecHalfHourChart
diff --git a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx
new file mode 100644
index 000000000..ebff1dd6d
--- /dev/null
+++ b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx
@@ -0,0 +1,80 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock'
+import { Provider } from 'react-redux'
+import configureStore from 'redux-mock-store'
+import * as reactRedux from 'react-redux'
+import { DateTime } from 'luxon'
+import ElecHalfHourMonthlyAnalysis from './ElecHalfHourMonthlyAnalysis'
+import { IconButton } from '@material-ui/core'
+
+jest.mock('cozy-ui/transpiled/react/I18n', () => {
+  return {
+    useI18n: jest.fn(() => {
+      return {
+        t: (str: string) => str,
+      }
+    }),
+  }
+})
+const mockcompareStepDate = jest.fn()
+jest.mock('services/dateChart.service', () => {
+  return jest.fn(() => {
+    return {
+      compareStepDate: mockcompareStepDate,
+    }
+  })
+})
+const mockStore = configureStore([])
+const mockUseSelector = jest.spyOn(reactRedux, 'useSelector')
+
+describe('ElecHalfHourMonthlyAnalysis component', () => {
+  it('should be rendered correctly', () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+      },
+    })
+    mockUseSelector.mockReturnValue(
+      DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+        zone: 'utc',
+      })
+    )
+    const wrapper = mount(
+      <Provider store={store}>
+        <ElecHalfHourMonthlyAnalysis
+          analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+            zone: 'utc',
+          })}
+        />
+      </Provider>
+    ).getElement()
+    expect(wrapper).toMatchSnapshot()
+  })
+  it('should change from weekend to week', async () => {
+    const store = mockStore({
+      ecolyo: {
+        global: globalStateData,
+      },
+    })
+    mockUseSelector.mockReturnValue(
+      DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+        zone: 'utc',
+      })
+    )
+    const wrapper = mount(
+      <Provider store={store}>
+        <ElecHalfHourMonthlyAnalysis
+          analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', {
+            zone: 'utc',
+          })}
+        />
+      </Provider>
+    )
+    wrapper
+      .find(IconButton)
+      .first()
+      .simulate('click')
+    expect(wrapper.find('.weekend')).toBeTruthy()
+  })
+})
diff --git a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx
new file mode 100644
index 000000000..be9c2d3df
--- /dev/null
+++ b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx
@@ -0,0 +1,219 @@
+import React, { useEffect, useState } from 'react'
+import { useI18n } from 'cozy-ui/transpiled/react/I18n'
+import LeftArrowIcon from 'assets/icons/ico/left-arrow.svg'
+import RigthArrowIcon from 'assets/icons/ico/right-arrow.svg'
+import MinIcon from 'assets/icons/ico/minimum.svg'
+import MaxPowerIcon from 'assets/icons/ico/maxPower.svg'
+import IconButton from '@material-ui/core/IconButton'
+import Icon from 'cozy-ui/transpiled/react/Icon'
+import { FluidType } from 'enum/fluid.enum'
+import iconEnedisLogo from 'assets/icons/visu/enedis-logo.svg'
+import { UserExplorationID } from 'enum/userExploration.enum'
+import { getNavPicto } from 'utils/picto'
+import { DateTime } from 'luxon'
+import { useClient } from 'cozy-client'
+import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service'
+import ConsumptionService from 'services/consumption.service'
+import {
+  AggregatedEnedisMonthlyDataloads,
+  EnedisMonthlyAnalysisData,
+} from 'models/enedisMonthlyAnalysis'
+import ElecHalfHourChart from './ElecHalfHourChart'
+import './elecHalfHourMonthlyAnalysis.scss'
+import StyledSpinner from 'components/CommonKit/Spinner/StyledSpinner'
+import { TimeStep } from 'enum/timeStep.enum'
+import { Button } from '@material-ui/core'
+import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
+import useExploration from 'components/Hooks/useExploration'
+import { FluidConfig } from 'models'
+import ConfigService from 'services/fluidConfig.service'
+
+interface ElecHalfHourMonthlyAnalysisProps {
+  analysisDate: DateTime
+}
+
+const ElecHalfHourMonthlyAnalysis: React.FC<ElecHalfHourMonthlyAnalysisProps> = ({
+  analysisDate,
+}: ElecHalfHourMonthlyAnalysisProps) => {
+  const { t } = useI18n()
+  const client = useClient()
+  const fluidConfig: Array<FluidConfig> = new ConfigService().getFluidConfig()
+  const [, setValidExploration] = useExploration()
+  const [isWeekend, setisWeekend] = useState(true)
+  const [isHalfHourActivated, setisHalfHourActivated] = useState(true)
+  const [isLoading, setisLoading] = useState(true)
+  const [monthDataloads, setMonthDataloads] = useState<
+    AggregatedEnedisMonthlyDataloads
+  >()
+  const [enedisAnalysisValues, setenedisAnalysisValues] = useState<
+    EnedisMonthlyAnalysisData
+  >()
+  const handleChangeWeek = () => {
+    setisWeekend(prev => !prev)
+  }
+
+  useEffect(() => {
+    let subscribed = true
+    async function getEnedisAnalysisData() {
+      const cs = new ConsumptionService(client)
+      const activateHalfHourLoad = await cs.checkDoctypeEntries(
+        FluidType.ELECTRICITY,
+        TimeStep.HALF_AN_HOUR
+      )
+      if (activateHalfHourLoad) {
+        const emas = new EnedisMonthlyAnalysisDataService(client)
+        const data = await emas.getEnedisMonthlyAnalysisByDate(
+          analysisDate.year,
+          analysisDate.month - 1
+        )
+        if (data && data.length) {
+          const aggregatedData = emas.aggregateValuesToDataLoad(data[0])
+          setenedisAnalysisValues(data[0])
+          setMonthDataloads(aggregatedData)
+        }
+      } else {
+        setisHalfHourActivated(false)
+      }
+      setisLoading(false)
+    }
+    if (subscribed) {
+      getEnedisAnalysisData()
+    }
+    return () => {
+      subscribed = false
+    }
+  }, [analysisDate, client])
+
+  return (
+    <div className="special-elec-container">
+      <Icon
+        className="elec-icon"
+        icon={getNavPicto(FluidType.ELECTRICITY, true, true)}
+        size={42}
+      />
+      <div className="text-18-normal title">{t('special_elec.title')}</div>
+      {isHalfHourActivated ? (
+        <>
+          <div className="navigator">
+            <IconButton
+              aria-label={t('consumption.accessibility.button_previous_value')}
+              onClick={handleChangeWeek}
+              className="arrow-prev"
+            >
+              <Icon icon={LeftArrowIcon} size={24} />
+            </IconButton>
+            <div className="average text-18-normal">
+              <div className="text-1">{t('special_elec.average')}</div>
+              <div className="text-2 text-18-bold">
+                {t('special_elec.weektype')}{' '}
+                <span className={isWeekend ? 'weekend' : 'week'}>
+                  {isWeekend
+                    ? t('special_elec.weekend')
+                    : t('special_elec.week')}
+                </span>
+              </div>
+            </div>
+            <IconButton
+              aria-label={t('consumption.accessibility.button_previous_value')}
+              onClick={handleChangeWeek}
+              className="arrow-next"
+            >
+              <Icon icon={RigthArrowIcon} size={24} />
+            </IconButton>
+          </div>
+          {!isLoading ? (
+            <>
+              {monthDataloads && (
+                <ElecHalfHourChart
+                  dataLoad={
+                    isWeekend ? monthDataloads.weekend : monthDataloads.week
+                  }
+                  isWeekend={isWeekend}
+                />
+              )}
+              {enedisAnalysisValues && (
+                <div className="min-max">
+                  <div className="container">
+                    <Icon icon={MinIcon} size={40} className="minIcon" />
+                    <div className="text">
+                      <div className="min text-18-normal">
+                        {t('special_elec.min')}
+                      </div>
+                      <div className="value text-18-bold">
+                        {enedisAnalysisValues.minLoad !== 0 &&
+                        enedisAnalysisValues.minLoad !== null ? (
+                          <>
+                            {enedisAnalysisValues.minLoad} <span> kWh</span>
+                          </>
+                        ) : (
+                          <span>----</span>
+                        )}
+                      </div>
+                    </div>
+                  </div>
+                  <div className="container">
+                    <Icon icon={MaxPowerIcon} size={40} className="minIcon" />
+                    <div className="text">
+                      <div className="min text-18-normal">
+                        {t('special_elec.maxPower')}
+                      </div>
+                      <div className="value text-18-bold">
+                        {enedisAnalysisValues.maxPower !== 0 &&
+                        enedisAnalysisValues.maxPower !== null ? (
+                          <>
+                            {enedisAnalysisValues.maxPower.toFixed(2)}
+                            <span> kVA</span>
+                          </>
+                        ) : (
+                          <span>----</span>
+                        )}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              )}
+            </>
+          ) : (
+            <div className="loader-container">
+              <StyledSpinner size="5em" fluidType={FluidType.ELECTRICITY} />
+            </div>
+          )}
+        </>
+      ) : (
+        <>
+          <div className="activation-text text-18-normal">
+            {t(`timestep.activate.enedis.no_consent_active.text_analysis`)}
+          </div>
+          <Button
+            aria-label={t(
+              `timestep.activate.enedis.no_consent_active.accessibility.button_activate`
+            )}
+            onClick={() => {
+              setValidExploration(UserExplorationID.EXPLORATION004)
+              window.open(fluidConfig[0].konnectorConfig.activation, '_blank')
+            }}
+            classes={{
+              root: 'btn-highlight',
+              label: 'text-16-bold',
+            }}
+          >
+            <div className="oauthform-button-content">
+              <div className="oauthform-button-content-icon">
+                <StyledIcon icon={iconEnedisLogo} size={48} />
+              </div>
+              <div className="oauthform-button-text text-18-bold">
+                <div>
+                  {t(
+                    `timestep.activate.enedis.no_consent_active.accessibility.button_activate`
+                  )}
+                </div>
+              </div>
+            </div>
+          </Button>
+        </>
+      )}
+    </div>
+  )
+}
+
+export default ElecHalfHourMonthlyAnalysis
diff --git a/src/components/Analysis/MonthlyAnalysis.tsx b/src/components/Analysis/MonthlyAnalysis.tsx
index 56640f42e..3576b5bbb 100644
--- a/src/components/Analysis/MonthlyAnalysis.tsx
+++ b/src/components/Analysis/MonthlyAnalysis.tsx
@@ -23,6 +23,7 @@ import { DateTime } from 'luxon'
 import MaxConsumptionCard from './MaxConsumptionCard'
 import AnalysisIcon from 'assets/icons/visu/analysis/analysis.svg'
 import TotalAnalysisChart from './TotalAnalysisChart'
+import ElecHalfHourMonthlyAnalysis from './ElecHalfHourMonthlyAnalysis'
 
 interface MonthlyAnalysisProps {
   analysisDate: DateTime
@@ -180,6 +181,13 @@ const MonthlyAnalysis: React.FC<MonthlyAnalysisProps> = ({
                   />
                 </div>
               </div>
+              {fluidTypes.includes(FluidType.ELECTRICITY) && (
+                <div className="analysis-content">
+                  <div className="card">
+                    <ElecHalfHourMonthlyAnalysis analysisDate={analysisDate} />
+                  </div>
+                </div>
+              )}
             </>
           ) : (
             <AnalysisErrorModal />
diff --git a/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap b/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap
new file mode 100644
index 000000000..4b4a2fe85
--- /dev/null
+++ b/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap
@@ -0,0 +1,44 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ElecHalfHourChart component should be rendered correctly 1`] = `
+<Provider
+  store={
+    Object {
+      "clearActions": [Function],
+      "dispatch": [Function],
+      "getActions": [Function],
+      "getState": [Function],
+      "replaceReducer": [Function],
+      "subscribe": [Function],
+    }
+  }
+>
+  <ElecHalfHourChart
+    dataLoad={
+      Array [
+        Object {
+          "date": "2021-09-23T00:00:00.000Z",
+          "value": 12,
+          "valueDetail": null,
+        },
+        Object {
+          "date": "2021-09-23T00:00:00.000Z",
+          "value": 12,
+          "valueDetail": null,
+        },
+        Object {
+          "date": "2021-09-23T00:00:00.000Z",
+          "value": 12,
+          "valueDetail": null,
+        },
+        Object {
+          "date": "2021-09-23T00:00:00.000Z",
+          "value": 12,
+          "valueDetail": null,
+        },
+      ]
+    }
+    isWeekend={true}
+  />
+</Provider>
+`;
diff --git a/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap b/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap
new file mode 100644
index 000000000..c0386f058
--- /dev/null
+++ b/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap
@@ -0,0 +1,20 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ElecHalfHourMonthlyAnalysis component should be rendered correctly 1`] = `
+<Provider
+  store={
+    Object {
+      "clearActions": [Function],
+      "dispatch": [Function],
+      "getActions": [Function],
+      "getState": [Function],
+      "replaceReducer": [Function],
+      "subscribe": [Function],
+    }
+  }
+>
+  <ElecHalfHourMonthlyAnalysis
+    analysisDate={"2021-07-01T00:00:00.000Z"}
+  />
+</Provider>
+`;
diff --git a/src/components/Analysis/elecHalfHourMonthlyAnalysis.scss b/src/components/Analysis/elecHalfHourMonthlyAnalysis.scss
new file mode 100644
index 000000000..30f836559
--- /dev/null
+++ b/src/components/Analysis/elecHalfHourMonthlyAnalysis.scss
@@ -0,0 +1,54 @@
+@import '../../styles/base/color';
+
+.special-elec-container {
+  color: white;
+  .elec-icon {
+    margin: auto;
+    display: block;
+  }
+  .title {
+    text-align: center;
+    margin-top: 1rem;
+    color: $grey-bright;
+  }
+  .navigator {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    text-align: center;
+    margin: 1rem 0;
+    .text-1 {
+      color: $grey-bright;
+    }
+    .week {
+      color: $elec-compare-color;
+    }
+    .weekend {
+      color: $multi-compare-color;
+    }
+  }
+  .minIcon {
+    margin-right: 0.7rem;
+  }
+  .activation-text {
+    margin: 1rem 0 0.7rem 0;
+  }
+  .oauthform-button-content {
+    display: flex;
+    padding: 0.5rem;
+  }
+  .oauthform-button-text {
+    text-align: left;
+    margin-left: 1rem;
+  }
+  .container {
+    margin-bottom: 1rem;
+  }
+  .loader-container {
+    text-align: center;
+  }
+}
+.graph-elec-half-hour {
+  height: 13rem;
+}
diff --git a/src/components/Charts/AxisRight.tsx b/src/components/Charts/AxisRight.tsx
index 7afffbba9..259eb53e5 100644
--- a/src/components/Charts/AxisRight.tsx
+++ b/src/components/Charts/AxisRight.tsx
@@ -11,6 +11,7 @@ interface AxisRightProps {
   width: number
   marginRight: number
   marginTop: number
+  isAnalysis?: boolean
 }
 
 const AxisRight = ({
@@ -19,6 +20,7 @@ const AxisRight = ({
   width,
   marginRight,
   marginTop,
+  isAnalysis,
 }: AxisRightProps) => {
   const { t } = useI18n()
   const fluidStyle =
@@ -31,7 +33,7 @@ const AxisRight = ({
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     select(yAxisRef.current!).call(
       axisRight(yScale)
-        .ticks(4)
+        .ticks(isAnalysis ? 0 : 4)
         .tickSize(-width)
         .tickSizeOuter(0)
         .tickFormat(d =>
diff --git a/src/components/Charts/Bar.tsx b/src/components/Charts/Bar.tsx
index f8db21834..0e259ed6e 100644
--- a/src/components/Charts/Bar.tsx
+++ b/src/components/Charts/Bar.tsx
@@ -27,6 +27,7 @@ interface BarProps {
   isSwitching: boolean
   isDuel?: boolean
   isMultiMissingFluid?: boolean
+  weekdays?: 'week' | 'weekend'
 }
 
 const Bar = ({
@@ -42,6 +43,7 @@ const Bar = ({
   isSwitching,
   isDuel,
   isMultiMissingFluid,
+  weekdays,
 }: BarProps) => {
   const dispatch = useDispatch()
   const { selectedDate } = useSelector((state: AppStore) => state.ecolyo.chart)
@@ -90,18 +92,18 @@ const Bar = ({
       )
 
   const barClass = clicked
-    ? `bar-${fluidStyle} selected bounce-${
+    ? `bar-${fluidStyle} ${weekdays} selected bounce-${
         browser && browser.name !== 'edge' ? '2' : '3'
       } delay`
     : isSelectedDate
     ? animationEnded
-      ? `bar-${fluidStyle} selected`
-      : `bar-${fluidStyle} selected bounce-${
+      ? `bar-${fluidStyle} ${weekdays} selected`
+      : `bar-${fluidStyle} ${weekdays} selected bounce-${
           browser && browser.name !== 'edge' ? '1' : '3'
         } delay--${index % 13}`
     : animationEnded
-    ? `bar-${fluidStyle} `
-    : `bar-${fluidStyle} bounce-${
+    ? `bar-${fluidStyle} ${weekdays}`
+    : `bar-${fluidStyle} ${weekdays} bounce-${
         browser && browser.name !== 'edge' ? '1' : '3'
       } delay--${index % 13}`
 
diff --git a/src/doctypes/com-grandlyon-enedis-maxpower.ts b/src/doctypes/com-grandlyon-enedis-maxpower.ts
new file mode 100644
index 000000000..f242f45ba
--- /dev/null
+++ b/src/doctypes/com-grandlyon-enedis-maxpower.ts
@@ -0,0 +1 @@
+export const ENEDIS_MAXPOWER_DOCTYPE = 'com.grandlyon.enedis.maxpower'
diff --git a/src/doctypes/com-grandlyon-enedis-monthly-analysis-data.ts b/src/doctypes/com-grandlyon-enedis-monthly-analysis-data.ts
new file mode 100644
index 000000000..90bde3263
--- /dev/null
+++ b/src/doctypes/com-grandlyon-enedis-monthly-analysis-data.ts
@@ -0,0 +1,2 @@
+export const ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE =
+  'com.grandlyon.enedis.monthly.analysis.data'
diff --git a/src/doctypes/index.ts b/src/doctypes/index.ts
index 14e1b0458..dc84679b3 100644
--- a/src/doctypes/index.ts
+++ b/src/doctypes/index.ts
@@ -25,6 +25,8 @@ import { GRDF_YEAR_DOCTYPE } from './com-grandlyon-grdf-year'
 import { GRDF_MONTH_DOCTYPE } from './com-grandlyon-grdf-month'
 import { EGL_YEAR_DOCTYPE } from './com-grandlyon-egl-year'
 import { EGL_MONTH_DOCTYPE } from './com-grandlyon-egl-month'
+import { ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE } from './com-grandlyon-enedis-monthly-analysis-data'
+import { ENEDIS_MAXPOWER_DOCTYPE } from './com-grandlyon-enedis-maxpower'
 
 // the documents schema, necessary for CozyClient
 const doctypes = {
@@ -152,12 +154,22 @@ const doctypes = {
     relationships: {},
   },
   usageevents: {
-    octype: USAGEEVENT_DOCTYPE,
+    doctype: USAGEEVENT_DOCTYPE,
     attributes: {},
     relationships: {},
   },
   schemas: {
-    octype: SCHEMAS_DOCTYPE,
+    doctype: SCHEMAS_DOCTYPE,
+    attributes: {},
+    relationships: {},
+  },
+  enedismonthlyanalysisdata: {
+    doctype: ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE,
+    attributes: {},
+    relationships: {},
+  },
+  enedismaxpower: {
+    doctype: ENEDIS_MAXPOWER_DOCTYPE,
     attributes: {},
     relationships: {},
   },
@@ -170,6 +182,8 @@ export * from './com-grandlyon-enedis-minute'
 export * from './com-grandlyon-enedis-day'
 export * from './com-grandlyon-enedis-month'
 export * from './com-grandlyon-enedis-year'
+export * from './com-grandlyon-enedis-monthly-analysis-data'
+export * from './com-grandlyon-enedis-maxpower'
 
 export * from './com-grandlyon-grdf-day'
 export * from './com-grandlyon-grdf-month'
diff --git a/src/locales/fr.json b/src/locales/fr.json
index e85ef82d5..03ae29f6d 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -106,6 +106,15 @@
     "estimation": "Comment sont estimés",
     "estimation2": "les prix ?"
   },
+  "special_elec": {
+    "title": "Spécial Électricité",
+    "average": "Conso moyenne",
+    "weektype": "un jour de ",
+    "week": "semaine",
+    "weekend": "week-end",
+    "min": "Consommation minimum",
+    "maxPower": "Puissance maximum atteinte"
+  },
   "auth": {
     "enedisgrandlyon": {
       "connect": {
@@ -927,7 +936,8 @@
           "label1": "Ré-activer sur mon compte Enedis",
           "accessibility": {
             "button_activate": "Aller sur mon compte Enedis"
-          }
+          },
+          "text_analysis": "Pour bénéficier d’une analyse approfondie de votre consommation électrique, il vous faut activer l’enregistrement de votre consommation horaire sur votre compte Enedis"
         }
       }
     },
diff --git a/src/models/enedisMonthlyAnalysis.ts b/src/models/enedisMonthlyAnalysis.ts
new file mode 100644
index 000000000..78d16f727
--- /dev/null
+++ b/src/models/enedisMonthlyAnalysis.ts
@@ -0,0 +1,14 @@
+import { Dataload } from 'models'
+
+export interface EnedisMonthlyAnalysisData {
+  weekDaysHalfHourAverageValues: number[]
+  weekEndDaysHalfHourAverageValues: number[]
+  month: number
+  year: number
+  minLoad: number | null
+  maxPower: number | null
+}
+export interface AggregatedEnedisMonthlyDataloads {
+  week: Dataload[]
+  weekend: Dataload[]
+}
diff --git a/src/models/maxPower.model.ts b/src/models/maxPower.model.ts
new file mode 100644
index 000000000..fe1784ef0
--- /dev/null
+++ b/src/models/maxPower.model.ts
@@ -0,0 +1,4 @@
+export interface MaxPowerEntity {
+  load: number
+  date: string
+}
diff --git a/src/services/consumption.service.ts b/src/services/consumption.service.ts
index 4e7fed0fd..7a2fac824 100644
--- a/src/services/consumption.service.ts
+++ b/src/services/consumption.service.ts
@@ -14,6 +14,8 @@ import QueryRunnerService from 'services/queryRunner.service'
 import ConsumptionValidatorService from 'services/consumptionValidator.service'
 import ConverterService from 'services/converter.service'
 import { ENEDIS_MINUTE_DOCTYPE } from 'doctypes'
+import { Doctype } from 'cozy-client/types/types'
+import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis'
 
 // eslint-disable-next-line @typescript-eslint/interface-name-prefix
 export interface ISingleFluidChartData {
@@ -523,4 +525,20 @@ export default class ConsumptionDataManager {
     const data = await client.query(query)
     return data.data
   }
+
+  /**
+   * Get the first entry of a given data doctype (enedis, grdf, egl)
+   * @param doctype
+   * @returns
+   */
+  public async getFirsDataDateFromDoctype(
+    doctype: Doctype
+  ): Promise<DataloadEntity[] | EnedisMonthlyAnalysisData[] | null> {
+    const query: QueryDefinition = Q(doctype)
+      .where({})
+      .sortBy([{ year: 'asc' }, { month: 'asc' }])
+      .limitBy(1)
+    const data = await this._client.query(query)
+    return data.data
+  }
 }
diff --git a/src/services/enedisMonthlyAnalysisData.service.spec.ts b/src/services/enedisMonthlyAnalysisData.service.spec.ts
new file mode 100644
index 000000000..4ed0ea216
--- /dev/null
+++ b/src/services/enedisMonthlyAnalysisData.service.spec.ts
@@ -0,0 +1,96 @@
+import { QueryResult } from 'cozy-client'
+import mockClient from '../../tests/__mocks__/client'
+import EnedisMonthlyAnalysisDataService from './enedisMonthlyAnalysisData.service'
+import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis'
+import {
+  maxPowerData,
+  mockDataLoadEnedisAnalysis,
+  mockEnedisMonthlyAnalysis,
+  mockEnedisMonthlyAnalysisArray,
+} from '../../tests/__mocks__/enedisMonthlyAnalysisData.mock'
+import { MaxPowerEntity } from 'models/maxPower.model'
+
+describe('Enedis Monthly Analysis service', () => {
+  const emas = new EnedisMonthlyAnalysisDataService(mockClient)
+
+  it('should return all Enedis Monthly Analysis', async () => {
+    const mockQueryResult: QueryResult<EnedisMonthlyAnalysisData[]> = {
+      data: mockEnedisMonthlyAnalysisArray,
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.query.mockResolvedValueOnce(mockQueryResult)
+    const result = await emas.getAllEnedisMonthlyAnalysisData()
+    expect(result).toEqual(mockEnedisMonthlyAnalysisArray)
+  })
+  it('should return no enedis analysis', async () => {
+    const mockQueryResult: QueryResult<EnedisMonthlyAnalysisData[]> = {
+      data: [],
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.query.mockResolvedValueOnce(mockQueryResult)
+    const result = await emas.getLastEnedisMonthlyAnalysis()
+    expect(result).toEqual([])
+  })
+  it('should return the last enedis analysis', async () => {
+    const mockQueryResult: QueryResult<EnedisMonthlyAnalysisData> = {
+      data: mockEnedisMonthlyAnalysis,
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.query.mockResolvedValueOnce(mockQueryResult)
+    const result = await emas.getLastEnedisMonthlyAnalysis()
+    expect(result).toEqual(mockEnedisMonthlyAnalysis)
+  })
+  it('should aggregate the analysis data to a dataload', () => {
+    const result = emas.aggregateValuesToDataLoad(
+      mockEnedisMonthlyAnalysisArray[0]
+    )
+    expect(result).toEqual(mockDataLoadEnedisAnalysis)
+  })
+  it('should get one analysis by Date', async () => {
+    const mockQueryResult1: QueryResult<EnedisMonthlyAnalysisData[]> = {
+      data: [mockEnedisMonthlyAnalysisArray[2]],
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.query.mockResolvedValueOnce(mockQueryResult1)
+    const result = await emas.getEnedisMonthlyAnalysisByDate(2021, 8)
+    expect(result).toEqual([mockEnedisMonthlyAnalysisArray[2]])
+  })
+  it('should create an enedis monthly analysis', async () => {
+    const mockQueryResult: QueryResult<EnedisMonthlyAnalysisData> = {
+      data: mockEnedisMonthlyAnalysisArray[0],
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.create.mockResolvedValueOnce(mockQueryResult)
+    const result = await emas.createEnedisMonthlyAnalysisData(
+      mockEnedisMonthlyAnalysisArray[0]
+    )
+    expect(result).toEqual(mockEnedisMonthlyAnalysisArray[0])
+  })
+  it('should fail create a enedis monthly analysis', async () => {
+    mockClient.create.mockRejectedValue(new Error())
+    await expect(
+      emas.createEnedisMonthlyAnalysisData(mockEnedisMonthlyAnalysisArray[0])
+    ).rejects.toThrow(new Error())
+  })
+  it('should get maxPower for a given month', async () => {
+    const mockQueryResult: QueryResult<MaxPowerEntity[]> = {
+      data: maxPowerData,
+      bookmark: '',
+      next: false,
+      skip: 0,
+    }
+    mockClient.query.mockResolvedValueOnce(mockQueryResult)
+    const result = await emas.getMaxPowerByDate(2021, 11)
+    expect(result).toEqual(maxPowerData)
+  })
+})
diff --git a/src/services/enedisMonthlyAnalysisData.service.ts b/src/services/enedisMonthlyAnalysisData.service.ts
new file mode 100644
index 000000000..dbb828bdb
--- /dev/null
+++ b/src/services/enedisMonthlyAnalysisData.service.ts
@@ -0,0 +1,158 @@
+import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client'
+
+import {
+  ENEDIS_MAXPOWER_DOCTYPE,
+  ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE,
+} from 'doctypes'
+import { DateTime } from 'luxon'
+import { Dataload } from 'models'
+import {
+  AggregatedEnedisMonthlyDataloads,
+  EnedisMonthlyAnalysisData,
+} from 'models/enedisMonthlyAnalysis'
+import { MaxPowerEntity } from 'models/maxPower.model'
+
+export default class EnedisMonthlyAnalysisDataService {
+  private readonly _client: Client
+
+  constructor(_client: Client) {
+    this._client = _client
+  }
+
+  /**
+   * Retrieve all exploration entities from db
+   * @returns {EnedisMonthlyAnalysisData[]}
+   */
+  public async getAllEnedisMonthlyAnalysisData(): Promise<
+    EnedisMonthlyAnalysisData[]
+  > {
+    const query: QueryDefinition = Q(ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE)
+    const {
+      data: enedisMonthlyAnalysisData,
+    }: QueryResult<EnedisMonthlyAnalysisData[]> = await this._client.query(
+      query
+    )
+    return enedisMonthlyAnalysisData
+  }
+
+  /**
+   * getLastEnedisMonthlyAnalysis
+   * @param {Client} client
+   * @returns {Promise<EnedisMonthlyAnalysisData[]>}
+   */
+  public async getLastEnedisMonthlyAnalysis(): Promise<
+    EnedisMonthlyAnalysisData[]
+  > {
+    const query: QueryDefinition = Q(ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE)
+      .where({})
+      .sortBy([{ year: 'desc' }, { month: 'desc' }])
+      .limitBy(1)
+    const data = await this._client.query(query)
+    return data.data
+  }
+
+  /**
+   * Aggregates Enedis Analysis data in order to create Dataload inhjectable in graph component
+   * @param {EnedisMonthlyAnalysisData} data
+   * @returns {AggregatedEnedisMonthlyDataloads}
+   */
+  public aggregateValuesToDataLoad = (
+    data: EnedisMonthlyAnalysisData
+  ): AggregatedEnedisMonthlyDataloads => {
+    const dataLoadWeekDays: Dataload[] = []
+    const dataLoadWeekEndDays: Dataload[] = []
+    data.weekDaysHalfHourAverageValues.forEach((value, index) => {
+      dataLoadWeekDays.push({
+        value: value,
+        valueDetail: null,
+        date: DateTime.fromObject({
+          year: data.year,
+          month: data.month,
+          minute: 0,
+        })
+          .setZone('utc', {
+            keepLocalTime: true,
+          })
+          .plus({ minute: 30 * index }),
+      })
+    })
+    data.weekEndDaysHalfHourAverageValues.forEach((value, index) => {
+      dataLoadWeekEndDays.push({
+        value: value,
+        valueDetail: null,
+        date: DateTime.fromObject({
+          year: data.year,
+          month: data.month,
+          minute: 0,
+        })
+          .setZone('utc', {
+            keepLocalTime: true,
+          })
+          .plus({ minute: 30 * index }),
+      })
+    })
+    return {
+      week: dataLoadWeekDays,
+      weekend: dataLoadWeekEndDays,
+    }
+  }
+
+  /**
+   * Get an enedis monthly analysis for given month and year
+   * @param {number} year
+   * @param {number} month
+   * @returns {Promise<EnedisMonthlyAnalysisData[]>}
+   */
+  public async getEnedisMonthlyAnalysisByDate(
+    year: number,
+    month: number
+  ): Promise<EnedisMonthlyAnalysisData[]> {
+    const query: QueryDefinition = Q(ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE)
+      .where({ year: year, month: month })
+      .sortBy([{ year: 'desc' }, { month: 'desc' }])
+      .limitBy(1)
+    const data = await this._client.query(query)
+
+    return data.data
+  }
+
+  /**
+   * Creates a new EnedisMonthlyAnalysis
+   * @param {EnedisMonthlyAnalysisData} newEnedisMonthlyAnalysisData
+   * @returns {Promise<EnedisMonthlyAnalysisData | null>}
+   */
+  public async createEnedisMonthlyAnalysisData(
+    newEnedisMonthlyAnalysisData: EnedisMonthlyAnalysisData
+  ): Promise<EnedisMonthlyAnalysisData | null> {
+    try {
+      const {
+        data: EnedisMonthlyAnalysis,
+      }: QueryResult<EnedisMonthlyAnalysisData> = await this._client.create(
+        ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE,
+        newEnedisMonthlyAnalysisData
+      )
+      return EnedisMonthlyAnalysis
+    } catch (error) {
+      console.log('Error creating new EnedisMonthlyAnalysis: ', error)
+      throw error
+    }
+  }
+
+  /**
+   * Get Max power for a given month and year
+   * @param {number} year
+   * @param {number} month
+   * @returns {Promise<MaxPowerEntity[]>}
+   */
+  public async getMaxPowerByDate(
+    year: number,
+    month: number
+  ): Promise<MaxPowerEntity[]> {
+    const query: QueryDefinition = Q(ENEDIS_MAXPOWER_DOCTYPE)
+      .where({ year: year, month: month })
+      .sortBy([{ year: 'desc' }, { month: 'desc' }])
+    const data = await this._client.query(query)
+
+    return data.data
+  }
+}
diff --git a/src/styles/components/_barchart.scss b/src/styles/components/_barchart.scss
index 70bd59c11..9cd721113 100644
--- a/src/styles/components/_barchart.scss
+++ b/src/styles/components/_barchart.scss
@@ -145,6 +145,20 @@
   stroke-width: 2;
   stroke: $multi-color;
 }
+.week {
+  fill: $elec-compare-color;
+  &.selected {
+    fill: $elec-compare-color;
+    filter: drop-shadow(0 -0.1rem 0.2rem $elec-compare-color);
+  }
+}
+.weekend {
+  fill: $multi-compare-color;
+  &.selected {
+    fill: $multi-compare-color;
+    filter: drop-shadow(0 -0.1rem 0.2rem $multi-color);
+  }
+}
 /** Animation **/
 .bounce-1 {
   animation-name: bounce-1;
diff --git a/src/styles/index.css b/src/styles/index.css
index e3d1fa6b6..202216d6e 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -789,8 +789,24 @@ p {
   stroke-width: 2;
   stroke: #e3b82a; }
 
+/* line 139, src/styles/components/_barchart.scss */
+.week {
+  fill: #e2bca1; }
+  /* line 141, src/styles/components/_barchart.scss */
+  .week.selected {
+    fill: #e2bca1;
+    filter: drop-shadow(0 -0.1rem 0.2rem #e2bca1); }
+
+/* line 146, src/styles/components/_barchart.scss */
+.weekend {
+  fill: #ffd597; }
+  /* line 148, src/styles/components/_barchart.scss */
+  .weekend.selected {
+    fill: #ffd597;
+    filter: drop-shadow(0 -0.1rem 0.2rem #e3b82a); }
+
 /** Animation **/
-/* line 140, src/styles/components/_barchart.scss */
+/* line 154, src/styles/components/_barchart.scss */
 .bounce-1 {
   animation-name: bounce-1;
   animation-timing-function: cubic-bezier(1, 1, 0.42, 1);
@@ -812,7 +828,7 @@ p {
   75% {
     transform: scale(1, 1); } }
 
-/* line 168, src/styles/components/_barchart.scss */
+/* line 182, src/styles/components/_barchart.scss */
 .bounce-2 {
   animation-name: bounce-2;
   animation-timing-function: cubic-bezier(1, 1, 0.42, 1);
@@ -834,7 +850,7 @@ p {
   75% {
     transform: scale(1, 1); } }
 
-/* line 196, src/styles/components/_barchart.scss */
+/* line 210, src/styles/components/_barchart.scss */
 .bounce-3 {
   animation-name: bounce-3;
   animation-timing-function: cubic-bezier(1, 1, 0.42, 1);
@@ -853,59 +869,59 @@ p {
     opacity: 1; } }
 
 /** Animatio ndelay **/
-/* line 218, src/styles/components/_barchart.scss */
+/* line 232, src/styles/components/_barchart.scss */
 .delay {
   animation-duration: 0.4s; }
 
-/* line 221, src/styles/components/_barchart.scss */
+/* line 235, src/styles/components/_barchart.scss */
 .delay--0 {
   animation-duration: 0.6s; }
 
-/* line 224, src/styles/components/_barchart.scss */
+/* line 238, src/styles/components/_barchart.scss */
 .delay--1 {
   animation-duration: 0.7s; }
 
-/* line 227, src/styles/components/_barchart.scss */
+/* line 241, src/styles/components/_barchart.scss */
 .delay--2 {
   animation-duration: 0.8s; }
 
-/* line 230, src/styles/components/_barchart.scss */
+/* line 244, src/styles/components/_barchart.scss */
 .delay--3 {
   animation-duration: 0.9s; }
 
-/* line 233, src/styles/components/_barchart.scss */
+/* line 247, src/styles/components/_barchart.scss */
 .delay--4 {
   animation-duration: 1s; }
 
-/* line 236, src/styles/components/_barchart.scss */
+/* line 250, src/styles/components/_barchart.scss */
 .delay--5 {
   animation-duration: 1.1s; }
 
-/* line 239, src/styles/components/_barchart.scss */
+/* line 253, src/styles/components/_barchart.scss */
 .delay--6 {
   animation-duration: 1.2s; }
 
-/* line 242, src/styles/components/_barchart.scss */
+/* line 256, src/styles/components/_barchart.scss */
 .delay--7 {
   animation-duration: 1.3s; }
 
-/* line 245, src/styles/components/_barchart.scss */
+/* line 259, src/styles/components/_barchart.scss */
 .delay--8 {
   animation-duration: 1.4s; }
 
-/* line 248, src/styles/components/_barchart.scss */
+/* line 262, src/styles/components/_barchart.scss */
 .delay--9 {
   animation-duration: 1.5s; }
 
-/* line 251, src/styles/components/_barchart.scss */
+/* line 265, src/styles/components/_barchart.scss */
 .delay--10 {
   animation-duration: 1.6s; }
 
-/* line 254, src/styles/components/_barchart.scss */
+/* line 268, src/styles/components/_barchart.scss */
 .delay--11 {
   animation-duration: 1.8s; }
 
-/* line 257, src/styles/components/_barchart.scss */
+/* line 271, src/styles/components/_barchart.scss */
 .delay--12 {
   animation-duration: 1.9s; }
 
diff --git a/src/targets/services/enedisHalfHourMonthlyAnalysis.ts b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts
new file mode 100644
index 000000000..3f03bbdcb
--- /dev/null
+++ b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts
@@ -0,0 +1,292 @@
+import logger from 'cozy-logger'
+import { Client } from 'cozy-client'
+import { DateTime } from 'luxon'
+import { Datachart, DataloadEntity, TimePeriod } from 'models'
+import { runService } from './service'
+import { FluidType } from 'enum/fluid.enum'
+import { TimeStep } from 'enum/timeStep.enum'
+import ConsumptionService from 'services/consumption.service'
+import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis'
+import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service'
+import {
+  ENEDIS_MINUTE_DOCTYPE,
+  ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE,
+} from 'doctypes'
+import { union } from 'lodash'
+
+const log = logger.namespace('report')
+
+interface EnedisMonthlyProps {
+  client: Client
+}
+
+/**
+ * Gets the minimum consumption value in a month
+ * @param weekEndValuesArray
+ * @param weekValuesArray
+ * @returns number
+ */
+const getMinMonthlyLoad = (
+  weekEndValuesArray: number[][],
+  weekValuesArray: number[][]
+): number => {
+  const totalArray = union(...weekEndValuesArray, ...weekValuesArray)
+  const filteredTotal = totalArray.filter(val => val !== -1 && val !== 0)
+  const minLoad = Math.min(...filteredTotal)
+  log('info', `Minimum value is ${minLoad} `)
+  return minLoad
+}
+
+/**
+ *
+ * @param monthlyArray
+ * @param dataChart
+ * @param isWeekend
+ * @returns
+ */
+const populateArrayWithTotalData = (
+  monthlyArray: number[][],
+  dataChart: Datachart,
+  isWeekend: boolean
+) => {
+  let halfHourDayData: number[]
+  if (isWeekend) {
+    // we only keep weekend days
+    halfHourDayData = dataChart.actualData
+      .filter(day => day.date.weekday === 6 || day.date.weekday === 7)
+      .map(day => day.value)
+  } else {
+    // we only keep weekdays
+    halfHourDayData = dataChart.actualData
+      .filter(day => day.date.weekday !== 6 && day.date.weekday !== 7)
+      .map(day => day.value)
+  }
+  if (halfHourDayData.length > 0) {
+    halfHourDayData.forEach((halfHourValue, index) => {
+      if (halfHourValue >= 0) {
+        if (!monthlyArray[index]) {
+          monthlyArray.push([])
+        }
+        monthlyArray[index] = [...monthlyArray[index], halfHourValue]
+      }
+    })
+  }
+}
+/**
+ * Gets max Power value for a given month
+ * @param month
+ * @param year
+ * @param client
+ * @returns
+ */
+const getMonthMaxPower = async (
+  month: number,
+  year: number,
+  client: Client
+) => {
+  const emas = new EnedisMonthlyAnalysisDataService(client)
+  log('info', `Fetching max power for month ${month} of year ${year}`)
+  const data = await emas.getMaxPowerByDate(year, month)
+  const maxPowerArray: number[] = []
+  if (data && data.length) {
+    for (const day of data) {
+      maxPowerArray.push(day.load)
+    }
+  }
+  return Math.max(...maxPowerArray)
+}
+/**
+ * Get the average arrays of half-hour value on a given month
+ * @param client
+ * @param month
+ * @param year
+ * @returns {Promise<MonthlyAveragesLoads>}
+ */
+const getEnedisMonthAnalysisData = async (
+  client: Client,
+  month: number,
+  year: number
+): Promise<EnedisMonthlyAnalysisData | undefined> => {
+  log('info', `Getting enedis analysis data for month ${month} of year ${year}`)
+
+  const timePeriod = {
+    startDate: DateTime.fromObject({ month: month, year: year }).startOf(
+      'month'
+    ),
+    endDate: DateTime.fromObject({ month: month, year: year }).endOf('month'),
+  }
+  const cs = new ConsumptionService(client)
+  const data = await cs.getGraphData(timePeriod, TimeStep.DAY, [
+    FluidType.ELECTRICITY,
+  ])
+  const monthlyAveragesLoads: EnedisMonthlyAnalysisData = {
+    weekDaysHalfHourAverageValues: [],
+    weekEndDaysHalfHourAverageValues: [],
+    minLoad: null,
+    maxPower: null,
+    month: month,
+    year: year,
+  }
+  if (data) {
+    // 48 is the number of halfhour entries in a day
+    const weekEndValuesArray: number[][] = new Array([])
+    const weekValuesArray: number[][] = new Array([])
+
+    for (const day of data.actualData) {
+      const timePeriod: TimePeriod = {
+        startDate: day.date.startOf('day'),
+        endDate: day.date.endOf('day'),
+      }
+      // for each day, we get its halfHour DataChart
+      // so we get 48 entries per day
+      const halfHourDayData = await cs.getGraphData(
+        timePeriod,
+        TimeStep.HALF_AN_HOUR,
+        [FluidType.ELECTRICITY]
+      )
+      if (halfHourDayData) {
+        populateArrayWithTotalData(weekEndValuesArray, halfHourDayData, true)
+        populateArrayWithTotalData(weekValuesArray, halfHourDayData, false)
+      }
+    }
+    monthlyAveragesLoads.minLoad = getMinMonthlyLoad(
+      weekValuesArray,
+      weekEndValuesArray
+    )
+    const arrAvg = (arr: number[]) =>
+      arr.reduce((a, b) => a + b, 0) / arr.length
+    // at this point we have an array of sums for each 48 half hour timestep
+    // so we calculate the average
+    const weekEndAverages: number[] = weekEndValuesArray.map(halfHourArray =>
+      arrAvg(halfHourArray)
+    )
+    // so we calculate the average
+    const weekAverages = weekValuesArray.map(halfHourArray =>
+      arrAvg(halfHourArray)
+    )
+    monthlyAveragesLoads.weekDaysHalfHourAverageValues = weekAverages
+    monthlyAveragesLoads.weekEndDaysHalfHourAverageValues = weekEndAverages
+    monthlyAveragesLoads.maxPower = await getMonthMaxPower(month, year, client)
+    return monthlyAveragesLoads
+  }
+}
+
+/**
+ * Synchronize enedis monthly analysis with database depending on if the service has already ran
+ * and if the enedis minute tracking has been activated
+ * @param {Client} client
+ * @returns
+ */
+const syncEnedisMonthlyAnalysisDataDoctype = async ({
+  client,
+}: EnedisMonthlyProps) => {
+  //SERVICE RUNS ONLY IF ENEDIS MINUTE IS ACTIVATED
+  const emas = new EnedisMonthlyAnalysisDataService(client)
+  const cs = new ConsumptionService(client)
+  const firstMinuteData = (await cs.getFirsDataDateFromDoctype(
+    ENEDIS_MINUTE_DOCTYPE
+  )) as DataloadEntity[]
+
+  const lastEnedisMonthlyAnalysis = await emas.getLastEnedisMonthlyAnalysis()
+  if (firstMinuteData) {
+    //First creates the analysis of the month - 1
+    log('info', 'Fetching last Enedis monthly Analysis...')
+    const firstMinuteDate = DateTime.fromObject({
+      year: firstMinuteData[0].year,
+      month: firstMinuteData[0].month,
+    }).startOf('month')
+    const today = DateTime.local().setZone('utc', {
+      keepLocalTime: true,
+    })
+    const analysisDate: DateTime = today.minus({
+      month: 1,
+    })
+
+    const data = await getEnedisMonthAnalysisData(
+      client,
+      analysisDate.month,
+      analysisDate.year
+    )
+    if (data) {
+      const created = await emas.createEnedisMonthlyAnalysisData(data)
+      if (created) {
+        log('success', 'Created successfully ! ')
+      } else {
+        log('error', 'Failed to create last Enedis monthly Analysis ')
+      }
+    }
+    log('info', 'Getting first endis half hour data date')
+
+    if (lastEnedisMonthlyAnalysis.length > 0) {
+      //If user has more than one entry (already synced), fetch the full history
+      const firstEnedisMonthlyAnalysis = (await cs.getFirsDataDateFromDoctype(
+        ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE
+      )) as EnedisMonthlyAnalysisData[]
+      if (
+        firstEnedisMonthlyAnalysis &&
+        firstEnedisMonthlyAnalysis[0].month === firstMinuteData[0].month &&
+        firstEnedisMonthlyAnalysis[0].year === firstMinuteData[0].year
+      ) {
+        log('info', 'Every Enedis Anlysis already synchronized')
+        return
+      } else if (firstEnedisMonthlyAnalysis) {
+        log(
+          'info',
+          'Doctype is partially completed, fetiching all available history'
+        )
+        const firstEnedisMonthlyAnalysisDate = DateTime.fromObject({
+          year: firstEnedisMonthlyAnalysis[0].year,
+          month: firstEnedisMonthlyAnalysis[0].month,
+        }).startOf('month')
+        const diffInmonths = Math.ceil(
+          firstEnedisMonthlyAnalysisDate.diff(firstMinuteDate, 'months').months
+        )
+        for (let i = 1; i < diffInmonths; i++) {
+          const analysistoCreate = firstEnedisMonthlyAnalysisDate.minus({
+            month: i,
+          })
+          const data = await getEnedisMonthAnalysisData(
+            client,
+            analysistoCreate.month,
+            analysistoCreate.year
+          )
+          if (data) {
+            await emas.createEnedisMonthlyAnalysisData(data)
+          }
+        }
+      }
+    } else {
+      //If user only have the last analysis available, fetch one year history
+      log(
+        'info',
+        'Doctype is empty, fetching history for one year maximum or until first enedis minute date'
+      )
+      const maximumDate = analysisDate.minus({ month: 12 }).startOf('month')
+      const diffInmonths = Math.ceil(
+        firstMinuteDate.diff(maximumDate, 'months').months
+      )
+      const limitDate = diffInmonths > 0 ? firstMinuteDate : maximumDate
+      const diffInMonthsWithLimitDate = Math.ceil(
+        analysisDate.diff(limitDate, 'months').months
+      )
+      for (let i = 1; i < diffInMonthsWithLimitDate; i++) {
+        const analysistoCreate = analysisDate.minus({ month: i })
+        const data = await getEnedisMonthAnalysisData(
+          client,
+          analysistoCreate.month,
+          analysistoCreate.year
+        )
+        if (data) {
+          await emas.createEnedisMonthlyAnalysisData(data)
+        }
+      }
+    }
+  } else {
+    log(
+      'info',
+      'Enedis Minute is not activated or there is no data yet in this doctype'
+    )
+  }
+}
+
+runService(syncEnedisMonthlyAnalysisDataDoctype)
diff --git a/tests/__mocks__/datachartData.mock.ts b/tests/__mocks__/datachartData.mock.ts
index 53627571c..de82121d0 100644
--- a/tests/__mocks__/datachartData.mock.ts
+++ b/tests/__mocks__/datachartData.mock.ts
@@ -56,6 +56,36 @@ export const baseDataLoad: Dataload = {
   value: 12,
   valueDetail: null,
 }
+export const dataLoadArray: Dataload[] = [
+  {
+    date: DateTime.fromISO('2021-09-23T00:00:00.000Z', {
+      zone: 'utc',
+    }),
+    value: 12,
+    valueDetail: null,
+  },
+  {
+    date: DateTime.fromISO('2021-09-23T00:00:00.000Z', {
+      zone: 'utc',
+    }),
+    value: 12,
+    valueDetail: null,
+  },
+  {
+    date: DateTime.fromISO('2021-09-23T00:00:00.000Z', {
+      zone: 'utc',
+    }),
+    value: 12,
+    valueDetail: null,
+  },
+  {
+    date: DateTime.fromISO('2021-09-23T00:00:00.000Z', {
+      zone: 'utc',
+    }),
+    value: 12,
+    valueDetail: null,
+  },
+]
 
 export const graphMonthData: Datachart = {
   actualData: [
diff --git a/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts b/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts
new file mode 100644
index 000000000..179c85a83
--- /dev/null
+++ b/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts
@@ -0,0 +1,106 @@
+import { DateTime } from 'luxon'
+import {
+  AggregatedEnedisMonthlyDataloads,
+  EnedisMonthlyAnalysisData,
+} from 'models/enedisMonthlyAnalysis'
+import { MaxPowerEntity } from 'models/maxPower.model'
+
+export const mockEnedisMonthlyAnalysis: EnedisMonthlyAnalysisData = {
+  weekDaysHalfHourAverageValues: [0.35, 0.34, 0.33, 0.32, 0.31, 0.3],
+  weekEndDaysHalfHourAverageValues: [0.25, 0.24, 0.23, 0.22, 0.21, 0.2],
+  month: 11,
+  year: 2021,
+  minLoad: 3,
+  maxPower: 2,
+}
+
+export const mockEnedisMonthlyAnalysisArray: EnedisMonthlyAnalysisData[] = [
+  {
+    weekDaysHalfHourAverageValues: [0.35, 0.34, 0.33],
+    weekEndDaysHalfHourAverageValues: [0.25, 0.24, 0.23],
+    month: 11,
+    year: 2021,
+    minLoad: 3,
+    maxPower: 2,
+  },
+  {
+    weekDaysHalfHourAverageValues: [0.35, 0.34, 0.33, 0.32, 0.31, 0.3],
+    weekEndDaysHalfHourAverageValues: [0.25, 0.24, 0.23, 0.22, 0.21, 0.2],
+    month: 10,
+    year: 2021,
+    minLoad: 3,
+    maxPower: 2,
+  },
+  {
+    weekDaysHalfHourAverageValues: [0.35, 0.34, 0.33, 0.32, 0.31, 0.3],
+    weekEndDaysHalfHourAverageValues: [0.25, 0.24, 0.23, 0.22, 0.21, 0.2],
+    month: 8,
+    year: 2021,
+    minLoad: 3,
+    maxPower: 2,
+  },
+  {
+    weekDaysHalfHourAverageValues: [0.35, 0.34, 0.33, 0.32, 0.31, 0.3],
+    weekEndDaysHalfHourAverageValues: [0.25, 0.24, 0.23, 0.22, 0.21, 0.2],
+    month: 7,
+    year: 2021,
+    minLoad: 3,
+    maxPower: 2,
+  },
+]
+
+export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = {
+  week: [
+    {
+      date: DateTime.fromISO('2021-11-01T00:00:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.35,
+      valueDetail: null,
+    },
+    {
+      date: DateTime.fromISO('2021-11-01T00:30:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.34,
+      valueDetail: null,
+    },
+    {
+      date: DateTime.fromISO('2021-11-01T01:00:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.33,
+      valueDetail: null,
+    },
+  ],
+  weekend: [
+    {
+      date: DateTime.fromISO('2021-11-01T00:00:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.25,
+      valueDetail: null,
+    },
+    {
+      date: DateTime.fromISO('2021-11-01T00:30:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.24,
+      valueDetail: null,
+    },
+    {
+      date: DateTime.fromISO('2021-11-01T01:00:00.000Z', {
+        zone: 'utc',
+      }),
+      value: 0.23,
+      valueDetail: null,
+    },
+  ],
+}
+
+export const maxPowerData: MaxPowerEntity[] = [
+  {
+    load: 5.3,
+    date: '20/11/2021',
+  },
+]
-- 
GitLab